1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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_NULL(¶ms[0]);
595 }
596 if (URL != NULL) {
597 ZVAL_STRING(¶ms[1], URL);
598 } else {
599 ZVAL_NULL(¶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
884 /* Enable the entity loader by default. This ensures that
885 * other threads/requests that might have disabled the loader
886 * do not affect the current request.
887 */
888 LIBXML(entity_loader_disabled) = 0;
889
890 return SUCCESS;
891 }
892
PHP_RSHUTDOWN_FUNCTION(libxml)893 static PHP_RSHUTDOWN_FUNCTION(libxml)
894 {
895 _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
896
897 return SUCCESS;
898 }
899
PHP_MSHUTDOWN_FUNCTION(libxml)900 static PHP_MSHUTDOWN_FUNCTION(libxml)
901 {
902 if (!_php_libxml_per_request_initialization) {
903 xmlSetGenericErrorFunc(NULL, NULL);
904
905 xmlParserInputBufferCreateFilenameDefault(NULL);
906 xmlOutputBufferCreateFilenameDefault(NULL);
907 }
908 php_libxml_shutdown();
909
910 return SUCCESS;
911 }
912
php_libxml_post_deactivate(void)913 static int php_libxml_post_deactivate(void)
914 {
915 /* reset libxml generic error handling */
916 if (_php_libxml_per_request_initialization) {
917 xmlSetGenericErrorFunc(NULL, NULL);
918
919 xmlParserInputBufferCreateFilenameDefault(NULL);
920 xmlOutputBufferCreateFilenameDefault(NULL);
921 }
922 xmlSetStructuredErrorFunc(NULL, NULL);
923
924 /* the steam_context resource will be released by resource list destructor */
925 ZVAL_UNDEF(&LIBXML(stream_context));
926 smart_str_free(&LIBXML(error_buffer));
927 if (LIBXML(error_list)) {
928 zend_llist_destroy(LIBXML(error_list));
929 efree(LIBXML(error_list));
930 LIBXML(error_list) = NULL;
931 }
932 xmlResetLastError();
933
934 return SUCCESS;
935 }
936
937
PHP_MINFO_FUNCTION(libxml)938 static PHP_MINFO_FUNCTION(libxml)
939 {
940 php_info_print_table_start();
941 php_info_print_table_row(2, "libXML support", "active");
942 php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
943 php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
944 php_info_print_table_row(2, "libXML streams", "enabled");
945 php_info_print_table_end();
946 }
947 /* }}} */
948
949 /* {{{ proto void libxml_set_streams_context(resource streams_context)
950 Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)951 static PHP_FUNCTION(libxml_set_streams_context)
952 {
953 zval *arg;
954
955 ZEND_PARSE_PARAMETERS_START(1, 1)
956 Z_PARAM_RESOURCE(arg)
957 ZEND_PARSE_PARAMETERS_END();
958
959 if (!Z_ISUNDEF(LIBXML(stream_context))) {
960 zval_ptr_dtor(&LIBXML(stream_context));
961 ZVAL_UNDEF(&LIBXML(stream_context));
962 }
963 ZVAL_COPY(&LIBXML(stream_context), arg);
964 }
965 /* }}} */
966
967 /* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
968 Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)969 static PHP_FUNCTION(libxml_use_internal_errors)
970 {
971 xmlStructuredErrorFunc current_handler;
972 zend_bool use_errors=0, retval;
973
974 ZEND_PARSE_PARAMETERS_START(0, 1)
975 Z_PARAM_OPTIONAL
976 Z_PARAM_BOOL(use_errors)
977 ZEND_PARSE_PARAMETERS_END();
978
979 current_handler = xmlStructuredError;
980 if (current_handler && current_handler == php_libxml_structured_error_handler) {
981 retval = 1;
982 } else {
983 retval = 0;
984 }
985
986 if (ZEND_NUM_ARGS() == 0) {
987 RETURN_BOOL(retval);
988 }
989
990 if (use_errors == 0) {
991 xmlSetStructuredErrorFunc(NULL, NULL);
992 if (LIBXML(error_list)) {
993 zend_llist_destroy(LIBXML(error_list));
994 efree(LIBXML(error_list));
995 LIBXML(error_list) = NULL;
996 }
997 } else {
998 xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
999 if (LIBXML(error_list) == NULL) {
1000 LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
1001 zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
1002 }
1003 }
1004 RETURN_BOOL(retval);
1005 }
1006 /* }}} */
1007
1008 /* {{{ proto object libxml_get_last_error()
1009 Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)1010 static PHP_FUNCTION(libxml_get_last_error)
1011 {
1012 xmlErrorPtr error;
1013
1014 error = xmlGetLastError();
1015
1016 if (error) {
1017 object_init_ex(return_value, libxmlerror_class_entry);
1018 add_property_long(return_value, "level", error->level);
1019 add_property_long(return_value, "code", error->code);
1020 add_property_long(return_value, "column", error->int2);
1021 if (error->message) {
1022 add_property_string(return_value, "message", error->message);
1023 } else {
1024 add_property_stringl(return_value, "message", "", 0);
1025 }
1026 if (error->file) {
1027 add_property_string(return_value, "file", error->file);
1028 } else {
1029 add_property_stringl(return_value, "file", "", 0);
1030 }
1031 add_property_long(return_value, "line", error->line);
1032 } else {
1033 RETURN_FALSE;
1034 }
1035 }
1036 /* }}} */
1037
1038 /* {{{ proto object libxml_get_errors()
1039 Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)1040 static PHP_FUNCTION(libxml_get_errors)
1041 {
1042
1043 xmlErrorPtr error;
1044
1045 if (array_init(return_value) == FAILURE) {
1046 RETURN_FALSE;
1047 }
1048
1049 if (LIBXML(error_list)) {
1050
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 }
1076 }
1077 /* }}} */
1078
1079 /* {{{ proto void libxml_clear_errors()
1080 Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)1081 static PHP_FUNCTION(libxml_clear_errors)
1082 {
1083 xmlResetLastError();
1084 if (LIBXML(error_list)) {
1085 zend_llist_clean(LIBXML(error_list));
1086 }
1087 }
1088 /* }}} */
1089
php_libxml_disable_entity_loader(zend_bool disable)1090 PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1091 {
1092 zend_bool old = LIBXML(entity_loader_disabled);
1093
1094 LIBXML(entity_loader_disabled) = disable;
1095 return old;
1096 } /* }}} */
1097
1098 /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1099 Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)1100 static PHP_FUNCTION(libxml_disable_entity_loader)
1101 {
1102 zend_bool disable = 1;
1103
1104 ZEND_PARSE_PARAMETERS_START(0, 1)
1105 Z_PARAM_OPTIONAL
1106 Z_PARAM_BOOL(disable)
1107 ZEND_PARSE_PARAMETERS_END();
1108
1109 RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1110 }
1111 /* }}} */
1112
1113 /* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1114 Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)1115 static PHP_FUNCTION(libxml_set_external_entity_loader)
1116 {
1117 zend_fcall_info fci;
1118 zend_fcall_info_cache fcc;
1119
1120 ZEND_PARSE_PARAMETERS_START(1, 1)
1121 Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1122 ZEND_PARSE_PARAMETERS_END();
1123
1124 _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1125
1126 if (fci.size > 0) { /* argument not null */
1127 LIBXML(entity_loader).fci = fci;
1128 Z_ADDREF(fci.function_name);
1129 if (fci.object != NULL) {
1130 ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1131 Z_ADDREF(LIBXML(entity_loader).object);
1132 }
1133 LIBXML(entity_loader).fcc = fcc;
1134 }
1135
1136 RETURN_TRUE;
1137 }
1138 /* }}} */
1139
1140 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)1141 int php_libxml_xmlCheckUTF8(const unsigned char *s)
1142 {
1143 int i;
1144 unsigned char c;
1145
1146 for (i = 0; (c = s[i++]);) {
1147 if ((c & 0x80) == 0) {
1148 } else if ((c & 0xe0) == 0xc0) {
1149 if ((s[i++] & 0xc0) != 0x80) {
1150 return 0;
1151 }
1152 } else if ((c & 0xf0) == 0xe0) {
1153 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1154 return 0;
1155 }
1156 } else if ((c & 0xf8) == 0xf0) {
1157 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1158 return 0;
1159 }
1160 } else {
1161 return 0;
1162 }
1163 }
1164 return 1;
1165 }
1166
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)1167 zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1168 {
1169 php_libxml_func_handler export_hnd;
1170
1171 /* Initialize in case this module hasn't been loaded yet */
1172 php_libxml_initialize();
1173 export_hnd.export_func = export_function;
1174
1175 return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1176 }
1177
php_libxml_import_node(zval * object)1178 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1179 {
1180 zend_class_entry *ce = NULL;
1181 xmlNodePtr node = NULL;
1182 php_libxml_func_handler *export_hnd;
1183
1184 if (Z_TYPE_P(object) == IS_OBJECT) {
1185 ce = Z_OBJCE_P(object);
1186 while (ce->parent != NULL) {
1187 ce = ce->parent;
1188 }
1189 if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1190 node = export_hnd->export_func(object);
1191 }
1192 }
1193 return node;
1194 }
1195
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data)1196 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1197 {
1198 int ret_refcount = -1;
1199
1200 if (object != NULL && node != NULL) {
1201 if (object->node != NULL) {
1202 if (object->node->node == node) {
1203 return object->node->refcount;
1204 } else {
1205 php_libxml_decrement_node_ptr(object);
1206 }
1207 }
1208 if (node->_private != NULL) {
1209 object->node = node->_private;
1210 ret_refcount = ++object->node->refcount;
1211 /* Only dom uses _private */
1212 if (object->node->_private == NULL) {
1213 object->node->_private = private_data;
1214 }
1215 } else {
1216 ret_refcount = 1;
1217 object->node = emalloc(sizeof(php_libxml_node_ptr));
1218 object->node->node = node;
1219 object->node->refcount = 1;
1220 object->node->_private = private_data;
1221 node->_private = object->node;
1222 }
1223 }
1224
1225 return ret_refcount;
1226 }
1227
php_libxml_decrement_node_ptr(php_libxml_node_object * object)1228 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1229 {
1230 int ret_refcount = -1;
1231 php_libxml_node_ptr *obj_node;
1232
1233 if (object != NULL && object->node != NULL) {
1234 obj_node = (php_libxml_node_ptr *) object->node;
1235 ret_refcount = --obj_node->refcount;
1236 if (ret_refcount == 0) {
1237 if (obj_node->node != NULL) {
1238 obj_node->node->_private = NULL;
1239 }
1240 efree(obj_node);
1241 }
1242 object->node = NULL;
1243 }
1244
1245 return ret_refcount;
1246 }
1247
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp)1248 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1249 {
1250 int ret_refcount = -1;
1251
1252 if (object->document != NULL) {
1253 object->document->refcount++;
1254 ret_refcount = object->document->refcount;
1255 } else if (docp != NULL) {
1256 ret_refcount = 1;
1257 object->document = emalloc(sizeof(php_libxml_ref_obj));
1258 object->document->ptr = docp;
1259 object->document->refcount = ret_refcount;
1260 object->document->doc_props = NULL;
1261 }
1262
1263 return ret_refcount;
1264 }
1265
php_libxml_decrement_doc_ref(php_libxml_node_object * object)1266 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1267 {
1268 int ret_refcount = -1;
1269
1270 if (object != NULL && object->document != NULL) {
1271 ret_refcount = --object->document->refcount;
1272 if (ret_refcount == 0) {
1273 if (object->document->ptr != NULL) {
1274 xmlFreeDoc((xmlDoc *) object->document->ptr);
1275 }
1276 if (object->document->doc_props != NULL) {
1277 if (object->document->doc_props->classmap) {
1278 zend_hash_destroy(object->document->doc_props->classmap);
1279 FREE_HASHTABLE(object->document->doc_props->classmap);
1280 }
1281 efree(object->document->doc_props);
1282 }
1283 efree(object->document);
1284 }
1285 object->document = NULL;
1286 }
1287
1288 return ret_refcount;
1289 }
1290
php_libxml_node_free_resource(xmlNodePtr node)1291 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1292 {
1293 if (!node) {
1294 return;
1295 }
1296
1297 switch (node->type) {
1298 case XML_DOCUMENT_NODE:
1299 case XML_HTML_DOCUMENT_NODE:
1300 break;
1301 default:
1302 if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1303 php_libxml_node_free_list((xmlNodePtr) node->children);
1304 switch (node->type) {
1305 /* Skip property freeing for the following types */
1306 case XML_ATTRIBUTE_DECL:
1307 case XML_DTD_NODE:
1308 case XML_DOCUMENT_TYPE_NODE:
1309 case XML_ENTITY_DECL:
1310 case XML_ATTRIBUTE_NODE:
1311 case XML_NAMESPACE_DECL:
1312 case XML_TEXT_NODE:
1313 break;
1314 default:
1315 php_libxml_node_free_list((xmlNodePtr) node->properties);
1316 }
1317 if (php_libxml_unregister_node(node) == 0) {
1318 node->doc = NULL;
1319 }
1320 php_libxml_node_free(node);
1321 } else {
1322 php_libxml_unregister_node(node);
1323 }
1324 }
1325 }
1326
php_libxml_node_decrement_resource(php_libxml_node_object * object)1327 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1328 {
1329 int ret_refcount = -1;
1330 xmlNodePtr nodep;
1331 php_libxml_node_ptr *obj_node;
1332
1333 if (object != NULL && object->node != NULL) {
1334 obj_node = (php_libxml_node_ptr *) object->node;
1335 nodep = object->node->node;
1336 ret_refcount = php_libxml_decrement_node_ptr(object);
1337 if (ret_refcount == 0) {
1338 php_libxml_node_free_resource(nodep);
1339 } else {
1340 if (obj_node && object == obj_node->_private) {
1341 obj_node->_private = NULL;
1342 }
1343 }
1344 }
1345 if (object != NULL && object->document != NULL) {
1346 /* Safe to call as if the resource were freed then doc pointer is NULL */
1347 php_libxml_decrement_doc_ref(object);
1348 }
1349 }
1350 /* }}} */
1351
1352 #if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1353 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1354 {
1355 return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1356 }
1357 #endif
1358
1359 #endif
1360
1361 /*
1362 * Local variables:
1363 * tab-width: 4
1364 * c-basic-offset: 4
1365 * End:
1366 * vim600: sw=4 ts=4 fdm=marker
1367 * vim<600: sw=4 ts=4
1368 */
1369