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