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