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