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