1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2015 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, *path_to_open = NULL;
297 void *ret_val = NULL;
298 int isescaped=0;
299 xmlURI *uri;
300
301 TSRMLS_FETCH();
302
303 uri = xmlParseURI(filename);
304 if (uri && (uri->scheme == NULL ||
305 (xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
306 resolved_path = xmlURIUnescapeString(filename, 0, NULL);
307 isescaped = 1;
308 #if LIBXML_VERSION >= 20902 && defined(PHP_WIN32)
309 /* Libxml 2.9.2 prefixes local paths with file:/ instead of file://,
310 thus the php stream wrapper will fail on a valid case. For this
311 reason the prefix is rather better cut off. */
312 {
313 size_t pre_len = sizeof("file:/") - 1;
314
315 if (strncasecmp(resolved_path, "file:/", pre_len) == 0
316 && '/' != resolved_path[pre_len]) {
317 xmlChar *tmp = xmlStrdup(resolved_path + pre_len);
318 xmlFree(resolved_path);
319 resolved_path = tmp;
320 }
321 }
322 #endif
323 } else {
324 resolved_path = (char *)filename;
325 }
326
327 if (uri) {
328 xmlFreeURI(uri);
329 }
330
331 if (resolved_path == NULL) {
332 return NULL;
333 }
334
335 /* logic copied from _php_stream_stat, but we only want to fail
336 if the wrapper supports stat, otherwise, figure it out from
337 the open. This logic is only to support hiding warnings
338 that the streams layer puts out at times, but for libxml we
339 may try to open files that don't exist, but it is not a failure
340 in xml processing (eg. DTD files) */
341 wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0 TSRMLS_CC);
342 if (wrapper && read_only && wrapper->wops->url_stat) {
343 if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL TSRMLS_CC) == -1) {
344 if (isescaped) {
345 xmlFree(resolved_path);
346 }
347 return NULL;
348 }
349 }
350
351 context = php_stream_context_from_zval(LIBXML(stream_context), 0);
352
353 ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, REPORT_ERRORS, NULL, context);
354 if (isescaped) {
355 xmlFree(resolved_path);
356 }
357 return ret_val;
358 }
359
php_libxml_streams_IO_open_read_wrapper(const char * filename)360 static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
361 {
362 return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
363 }
364
php_libxml_streams_IO_open_write_wrapper(const char * filename)365 static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
366 {
367 return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
368 }
369
php_libxml_streams_IO_read(void * context,char * buffer,int len)370 static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
371 {
372 TSRMLS_FETCH();
373 return php_stream_read((php_stream*)context, buffer, len);
374 }
375
php_libxml_streams_IO_write(void * context,const char * buffer,int len)376 static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
377 {
378 TSRMLS_FETCH();
379 return php_stream_write((php_stream*)context, buffer, len);
380 }
381
php_libxml_streams_IO_close(void * context)382 static int php_libxml_streams_IO_close(void *context)
383 {
384 TSRMLS_FETCH();
385 return php_stream_close((php_stream*)context);
386 }
387
388 static xmlParserInputBufferPtr
php_libxml_input_buffer_create_filename(const char * URI,xmlCharEncoding enc)389 php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
390 {
391 xmlParserInputBufferPtr ret;
392 void *context = NULL;
393 TSRMLS_FETCH();
394
395 if (LIBXML(entity_loader_disabled)) {
396 return NULL;
397 }
398
399 if (URI == NULL)
400 return(NULL);
401
402 context = php_libxml_streams_IO_open_read_wrapper(URI);
403
404 if (context == NULL) {
405 return(NULL);
406 }
407
408 /* Allocate the Input buffer front-end. */
409 ret = xmlAllocParserInputBuffer(enc);
410 if (ret != NULL) {
411 ret->context = context;
412 ret->readcallback = php_libxml_streams_IO_read;
413 ret->closecallback = php_libxml_streams_IO_close;
414 } else
415 php_libxml_streams_IO_close(context);
416
417 return(ret);
418 }
419
420 static xmlOutputBufferPtr
php_libxml_output_buffer_create_filename(const char * URI,xmlCharEncodingHandlerPtr encoder,int compression ATTRIBUTE_UNUSED)421 php_libxml_output_buffer_create_filename(const char *URI,
422 xmlCharEncodingHandlerPtr encoder,
423 int compression ATTRIBUTE_UNUSED)
424 {
425 xmlOutputBufferPtr ret;
426 xmlURIPtr puri;
427 void *context = NULL;
428 char *unescaped = NULL;
429
430 if (URI == NULL)
431 return(NULL);
432
433 puri = xmlParseURI(URI);
434 if (puri != NULL) {
435 if (puri->scheme != NULL)
436 unescaped = xmlURIUnescapeString(URI, 0, NULL);
437 xmlFreeURI(puri);
438 }
439
440 if (unescaped != NULL) {
441 context = php_libxml_streams_IO_open_write_wrapper(unescaped);
442 xmlFree(unescaped);
443 }
444
445 /* try with a non-escaped URI this may be a strange filename */
446 if (context == NULL) {
447 context = php_libxml_streams_IO_open_write_wrapper(URI);
448 }
449
450 if (context == NULL) {
451 return(NULL);
452 }
453
454 /* Allocate the Output buffer front-end. */
455 ret = xmlAllocOutputBuffer(encoder);
456 if (ret != NULL) {
457 ret->context = context;
458 ret->writecallback = php_libxml_streams_IO_write;
459 ret->closecallback = php_libxml_streams_IO_close;
460 }
461
462 return(ret);
463 }
464
_php_libxml_free_error(xmlErrorPtr error)465 static int _php_libxml_free_error(xmlErrorPtr error)
466 {
467 /* This will free the libxml alloc'd memory */
468 xmlResetError(error);
469 return 1;
470 }
471
_php_list_set_error_structure(xmlErrorPtr error,const char * msg)472 static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
473 {
474 xmlError error_copy;
475 int ret;
476
477 TSRMLS_FETCH();
478
479 memset(&error_copy, 0, sizeof(xmlError));
480
481 if (error) {
482 ret = xmlCopyError(error, &error_copy);
483 } else {
484 error_copy.domain = 0;
485 error_copy.code = XML_ERR_INTERNAL_ERROR;
486 error_copy.level = XML_ERR_ERROR;
487 error_copy.line = 0;
488 error_copy.node = NULL;
489 error_copy.int1 = 0;
490 error_copy.int2 = 0;
491 error_copy.ctxt = NULL;
492 error_copy.message = xmlStrdup(msg);
493 error_copy.file = NULL;
494 error_copy.str1 = NULL;
495 error_copy.str2 = NULL;
496 error_copy.str3 = NULL;
497 ret = 0;
498 }
499
500 if (ret == 0) {
501 zend_llist_add_element(LIBXML(error_list), &error_copy);
502 }
503 }
504
php_libxml_ctx_error_level(int level,void * ctx,const char * msg TSRMLS_DC)505 static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg TSRMLS_DC)
506 {
507 xmlParserCtxtPtr parser;
508
509 parser = (xmlParserCtxtPtr) ctx;
510
511 if (parser != NULL && parser->input != NULL) {
512 if (parser->input->filename) {
513 php_error_docref(NULL TSRMLS_CC, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
514 } else {
515 php_error_docref(NULL TSRMLS_CC, level, "%s in Entity, line: %d", msg, parser->input->line);
516 }
517 }
518 }
519
php_libxml_issue_error(int level,const char * msg TSRMLS_DC)520 void php_libxml_issue_error(int level, const char *msg TSRMLS_DC)
521 {
522 if (LIBXML(error_list)) {
523 _php_list_set_error_structure(NULL, msg);
524 } else {
525 php_error_docref(NULL TSRMLS_CC, level, "%s", msg);
526 }
527 }
528
php_libxml_internal_error_handler(int error_type,void * ctx,const char ** msg,va_list ap)529 static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
530 {
531 char *buf;
532 int len, len_iter, output = 0;
533
534 TSRMLS_FETCH();
535
536 len = vspprintf(&buf, 0, *msg, ap);
537 len_iter = len;
538
539 /* remove any trailing \n */
540 while (len_iter && buf[--len_iter] == '\n') {
541 buf[len_iter] = '\0';
542 output = 1;
543 }
544
545 smart_str_appendl(&LIBXML(error_buffer), buf, len);
546
547 efree(buf);
548
549 if (output == 1) {
550 if (LIBXML(error_list)) {
551 _php_list_set_error_structure(NULL, LIBXML(error_buffer).c);
552 } else {
553 switch (error_type) {
554 case PHP_LIBXML_CTX_ERROR:
555 php_libxml_ctx_error_level(E_WARNING, ctx, LIBXML(error_buffer).c TSRMLS_CC);
556 break;
557 case PHP_LIBXML_CTX_WARNING:
558 php_libxml_ctx_error_level(E_NOTICE, ctx, LIBXML(error_buffer).c TSRMLS_CC);
559 break;
560 default:
561 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", LIBXML(error_buffer).c);
562 }
563 }
564 smart_str_free(&LIBXML(error_buffer));
565 }
566 }
567
_php_libxml_external_entity_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)568 static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
569 const char *ID, xmlParserCtxtPtr context)
570 {
571 xmlParserInputPtr ret = NULL;
572 const char *resource = NULL;
573 zval *public = NULL,
574 *system = NULL,
575 *ctxzv = NULL,
576 **params[] = {&public, &system, &ctxzv},
577 *retval_ptr = NULL;
578 int retval;
579 zend_fcall_info *fci;
580 TSRMLS_FETCH();
581
582 fci = &LIBXML(entity_loader).fci;
583
584 if (fci->size == 0) {
585 /* no custom user-land callback set up; delegate to original loader */
586 return _php_libxml_default_entity_loader(URL, ID, context);
587 }
588
589 ALLOC_INIT_ZVAL(public);
590 if (ID != NULL) {
591 ZVAL_STRING(public, ID, 1);
592 }
593 ALLOC_INIT_ZVAL(system);
594 if (URL != NULL) {
595 ZVAL_STRING(system, URL, 1);
596 }
597 MAKE_STD_ZVAL(ctxzv);
598 array_init_size(ctxzv, 4);
599
600 #define ADD_NULL_OR_STRING_KEY(memb) \
601 if (context->memb == NULL) { \
602 add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
603 } else { \
604 add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
605 (char *)context->memb, 1); \
606 }
607
608 ADD_NULL_OR_STRING_KEY(directory)
609 ADD_NULL_OR_STRING_KEY(intSubName)
610 ADD_NULL_OR_STRING_KEY(extSubURI)
611 ADD_NULL_OR_STRING_KEY(extSubSystem)
612
613 #undef ADD_NULL_OR_STRING_KEY
614
615 fci->retval_ptr_ptr = &retval_ptr;
616 fci->params = params;
617 fci->param_count = sizeof(params)/sizeof(*params);
618 fci->no_separation = 1;
619
620 retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
621 if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
622 php_libxml_ctx_error(context,
623 "Call to user entity loader callback '%s' has failed",
624 fci->function_name);
625 } else {
626 retval_ptr = *fci->retval_ptr_ptr;
627 if (retval_ptr == NULL) {
628 php_libxml_ctx_error(context,
629 "Call to user entity loader callback '%s' has failed; "
630 "probably it has thrown an exception",
631 fci->function_name);
632 } else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
633 is_string:
634 resource = Z_STRVAL_P(retval_ptr);
635 } else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
636 php_stream *stream;
637 php_stream_from_zval_no_verify(stream, &retval_ptr);
638 if (stream == NULL) {
639 php_libxml_ctx_error(context,
640 "The user entity loader callback '%s' has returned a "
641 "resource, but it is not a stream",
642 fci->function_name);
643 } else {
644 /* TODO: allow storing the encoding in the stream context? */
645 xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
646 xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
647 if (pib == NULL) {
648 php_libxml_ctx_error(context, "Could not allocate parser "
649 "input buffer");
650 } else {
651 /* make stream not being closed when the zval is freed */
652 zend_list_addref(stream->rsrc_id);
653 pib->context = stream;
654 pib->readcallback = php_libxml_streams_IO_read;
655 pib->closecallback = php_libxml_streams_IO_close;
656
657 ret = xmlNewIOInputStream(context, pib, enc);
658 if (ret == NULL) {
659 xmlFreeParserInputBuffer(pib);
660 }
661 }
662 }
663 } else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
664 /* retval not string nor resource nor null; convert to string */
665 SEPARATE_ZVAL(&retval_ptr);
666 convert_to_string(retval_ptr);
667 goto is_string;
668 } /* else is null; don't try anything */
669 }
670
671 if (ret == NULL) {
672 if (resource == NULL) {
673 if (ID == NULL) {
674 ID = "NULL";
675 }
676 php_libxml_ctx_error(context,
677 "Failed to load external entity \"%s\"\n", ID);
678 } else {
679 /* we got the resource in the form of a string; open it */
680 ret = xmlNewInputFromFile(context, resource);
681 }
682 }
683
684 zval_ptr_dtor(&public);
685 zval_ptr_dtor(&system);
686 zval_ptr_dtor(&ctxzv);
687 if (retval_ptr != NULL) {
688 zval_ptr_dtor(&retval_ptr);
689 }
690 return ret;
691 }
692
_php_libxml_pre_ext_ent_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)693 static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
694 const char *ID, xmlParserCtxtPtr context)
695 {
696 TSRMLS_FETCH();
697
698 /* Check whether we're running in a PHP context, since the entity loader
699 * we've defined is an application level (true global) setting.
700 * If we are, we also want to check whether we've finished activating
701 * the modules (RINIT phase). Using our external entity loader during a
702 * RINIT should not be problem per se (though during MINIT it is, because
703 * we don't even have a resource list by then), but then whether one
704 * extension would be using the custom external entity loader or not
705 * could depend on extension loading order
706 * (if _php_libxml_per_request_initialization */
707 if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
708 return _php_libxml_external_entity_loader(URL, ID, context);
709 } else {
710 return _php_libxml_default_entity_loader(URL, ID, context);
711 }
712 }
713
php_libxml_ctx_error(void * ctx,const char * msg,...)714 PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
715 {
716 va_list args;
717 va_start(args, msg);
718 php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
719 va_end(args);
720 }
721
php_libxml_ctx_warning(void * ctx,const char * msg,...)722 PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
723 {
724 va_list args;
725 va_start(args, msg);
726 php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
727 va_end(args);
728 }
729
php_libxml_structured_error_handler(void * userData,xmlErrorPtr error)730 PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
731 {
732 _php_list_set_error_structure(error, NULL);
733
734 return;
735 }
736
php_libxml_error_handler(void * ctx,const char * msg,...)737 PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
738 {
739 va_list args;
740 va_start(args, msg);
741 php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
742 va_end(args);
743 }
744
745
php_libxml_initialize(void)746 PHP_LIBXML_API void php_libxml_initialize(void)
747 {
748 if (!_php_libxml_initialized) {
749 /* we should be the only one's to ever init!! */
750 xmlInitParser();
751
752 _php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
753 xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
754
755 zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
756
757 _php_libxml_initialized = 1;
758 }
759 }
760
php_libxml_shutdown(void)761 PHP_LIBXML_API void php_libxml_shutdown(void)
762 {
763 if (_php_libxml_initialized) {
764 #if defined(LIBXML_SCHEMAS_ENABLED)
765 xmlRelaxNGCleanupTypes();
766 #endif
767 xmlCleanupParser();
768 zend_hash_destroy(&php_libxml_exports);
769
770 xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
771 _php_libxml_initialized = 0;
772 }
773 }
774
php_libxml_switch_context(zval * context TSRMLS_DC)775 PHP_LIBXML_API zval *php_libxml_switch_context(zval *context TSRMLS_DC)
776 {
777 zval *oldcontext;
778
779 oldcontext = LIBXML(stream_context);
780 LIBXML(stream_context) = context;
781 return oldcontext;
782
783 }
784
PHP_MINIT_FUNCTION(libxml)785 static PHP_MINIT_FUNCTION(libxml)
786 {
787 zend_class_entry ce;
788
789 php_libxml_initialize();
790
791 REGISTER_LONG_CONSTANT("LIBXML_VERSION", LIBXML_VERSION, CONST_CS | CONST_PERSISTENT);
792 REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION", LIBXML_DOTTED_VERSION, CONST_CS | CONST_PERSISTENT);
793 REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION", (char *)xmlParserVersion, CONST_CS | CONST_PERSISTENT);
794
795 /* For use with loading xml */
796 REGISTER_LONG_CONSTANT("LIBXML_NOENT", XML_PARSE_NOENT, CONST_CS | CONST_PERSISTENT);
797 REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD", XML_PARSE_DTDLOAD, CONST_CS | CONST_PERSISTENT);
798 REGISTER_LONG_CONSTANT("LIBXML_DTDATTR", XML_PARSE_DTDATTR, CONST_CS | CONST_PERSISTENT);
799 REGISTER_LONG_CONSTANT("LIBXML_DTDVALID", XML_PARSE_DTDVALID, CONST_CS | CONST_PERSISTENT);
800 REGISTER_LONG_CONSTANT("LIBXML_NOERROR", XML_PARSE_NOERROR, CONST_CS | CONST_PERSISTENT);
801 REGISTER_LONG_CONSTANT("LIBXML_NOWARNING", XML_PARSE_NOWARNING, CONST_CS | CONST_PERSISTENT);
802 REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS", XML_PARSE_NOBLANKS, CONST_CS | CONST_PERSISTENT);
803 REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE", XML_PARSE_XINCLUDE, CONST_CS | CONST_PERSISTENT);
804 REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN", XML_PARSE_NSCLEAN, CONST_CS | CONST_PERSISTENT);
805 REGISTER_LONG_CONSTANT("LIBXML_NOCDATA", XML_PARSE_NOCDATA, CONST_CS | CONST_PERSISTENT);
806 REGISTER_LONG_CONSTANT("LIBXML_NONET", XML_PARSE_NONET, CONST_CS | CONST_PERSISTENT);
807 REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC", XML_PARSE_PEDANTIC, CONST_CS | CONST_PERSISTENT);
808 #if LIBXML_VERSION >= 20621
809 REGISTER_LONG_CONSTANT("LIBXML_COMPACT", XML_PARSE_COMPACT, CONST_CS | CONST_PERSISTENT);
810 REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL", XML_SAVE_NO_DECL, CONST_CS | CONST_PERSISTENT);
811 #endif
812 #if LIBXML_VERSION >= 20703
813 REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE", XML_PARSE_HUGE, CONST_CS | CONST_PERSISTENT);
814 #endif
815 REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG", LIBXML_SAVE_NOEMPTYTAG, CONST_CS | CONST_PERSISTENT);
816
817 /* Schema validation options */
818 #if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION >= 20614
819 REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE", XML_SCHEMA_VAL_VC_I_CREATE, CONST_CS | CONST_PERSISTENT);
820 #endif
821
822 /* Additional constants for use with loading html */
823 #if LIBXML_VERSION >= 20707
824 REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED", HTML_PARSE_NOIMPLIED, CONST_CS | CONST_PERSISTENT);
825 #endif
826
827 #if LIBXML_VERSION >= 20708
828 REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD", HTML_PARSE_NODEFDTD, CONST_CS | CONST_PERSISTENT);
829 #endif
830
831 /* Error levels */
832 REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE", XML_ERR_NONE, CONST_CS | CONST_PERSISTENT);
833 REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING", XML_ERR_WARNING, CONST_CS | CONST_PERSISTENT);
834 REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR", XML_ERR_ERROR, CONST_CS | CONST_PERSISTENT);
835 REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL", XML_ERR_FATAL, CONST_CS | CONST_PERSISTENT);
836
837 INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
838 libxmlerror_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
839
840 if (sapi_module.name) {
841 static const char * const supported_sapis[] = {
842 "cgi-fcgi",
843 "fpm-fcgi",
844 "litespeed",
845 NULL
846 };
847 const char * const *sapi_name;
848
849 for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
850 if (strcmp(sapi_module.name, *sapi_name) == 0) {
851 _php_libxml_per_request_initialization = 0;
852 break;
853 }
854 }
855 }
856
857 if (!_php_libxml_per_request_initialization) {
858 /* report errors via handler rather than stderr */
859 xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
860 xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
861 xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
862 }
863
864 return SUCCESS;
865 }
866
867
PHP_RINIT_FUNCTION(libxml)868 static PHP_RINIT_FUNCTION(libxml)
869 {
870 if (_php_libxml_per_request_initialization) {
871 /* report errors via handler rather than stderr */
872 xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
873 xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
874 xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
875
876 /* Enable the entity loader by default. This ensure that
877 * other threads/requests that might have disable the loader
878 * do not affect the current request.
879 */
880 LIBXML(entity_loader_disabled) = 0;
881 }
882 return SUCCESS;
883 }
884
885
PHP_MSHUTDOWN_FUNCTION(libxml)886 static PHP_MSHUTDOWN_FUNCTION(libxml)
887 {
888 if (!_php_libxml_per_request_initialization) {
889 xmlSetGenericErrorFunc(NULL, NULL);
890
891 xmlParserInputBufferCreateFilenameDefault(NULL);
892 xmlOutputBufferCreateFilenameDefault(NULL);
893 }
894 php_libxml_shutdown();
895
896 return SUCCESS;
897 }
898
php_libxml_post_deactivate(void)899 static int php_libxml_post_deactivate(void)
900 {
901 TSRMLS_FETCH();
902 /* reset libxml generic error handling */
903 if (_php_libxml_per_request_initialization) {
904 xmlSetGenericErrorFunc(NULL, NULL);
905
906 xmlParserInputBufferCreateFilenameDefault(NULL);
907 xmlOutputBufferCreateFilenameDefault(NULL);
908 }
909 xmlSetStructuredErrorFunc(NULL, NULL);
910
911 if (LIBXML(stream_context)) {
912 /* the steam_context resource will be released by resource list destructor */
913 efree(LIBXML(stream_context));
914 LIBXML(stream_context) = NULL;
915 }
916 smart_str_free(&LIBXML(error_buffer));
917 if (LIBXML(error_list)) {
918 zend_llist_destroy(LIBXML(error_list));
919 efree(LIBXML(error_list));
920 LIBXML(error_list) = NULL;
921 }
922 xmlResetLastError();
923
924 _php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
925
926 return SUCCESS;
927 }
928
929
PHP_MINFO_FUNCTION(libxml)930 static PHP_MINFO_FUNCTION(libxml)
931 {
932 php_info_print_table_start();
933 php_info_print_table_row(2, "libXML support", "active");
934 php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
935 php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
936 php_info_print_table_row(2, "libXML streams", "enabled");
937 php_info_print_table_end();
938 }
939 /* }}} */
940
941 /* {{{ proto void libxml_set_streams_context(resource streams_context)
942 Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)943 static PHP_FUNCTION(libxml_set_streams_context)
944 {
945 zval *arg;
946
947 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg) == FAILURE) {
948 return;
949 }
950 if (LIBXML(stream_context)) {
951 zval_ptr_dtor(&LIBXML(stream_context));
952 LIBXML(stream_context) = NULL;
953 }
954 Z_ADDREF_P(arg);
955 LIBXML(stream_context) = arg;
956 }
957 /* }}} */
958
959 /* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
960 Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)961 static PHP_FUNCTION(libxml_use_internal_errors)
962 {
963 xmlStructuredErrorFunc current_handler;
964 zend_bool use_errors=0, retval;
965
966 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &use_errors) == FAILURE) {
967 return;
968 }
969
970 current_handler = xmlStructuredError;
971 if (current_handler && current_handler == php_libxml_structured_error_handler) {
972 retval = 1;
973 } else {
974 retval = 0;
975 }
976
977 if (ZEND_NUM_ARGS() == 0) {
978 RETURN_BOOL(retval);
979 }
980
981 if (use_errors == 0) {
982 xmlSetStructuredErrorFunc(NULL, NULL);
983 if (LIBXML(error_list)) {
984 zend_llist_destroy(LIBXML(error_list));
985 efree(LIBXML(error_list));
986 LIBXML(error_list) = NULL;
987 }
988 } else {
989 xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
990 if (LIBXML(error_list) == NULL) {
991 LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
992 zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
993 }
994 }
995 RETURN_BOOL(retval);
996 }
997 /* }}} */
998
999 /* {{{ proto object libxml_get_last_error()
1000 Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)1001 static PHP_FUNCTION(libxml_get_last_error)
1002 {
1003 xmlErrorPtr error;
1004
1005 error = xmlGetLastError();
1006
1007 if (error) {
1008 object_init_ex(return_value, libxmlerror_class_entry);
1009 add_property_long(return_value, "level", error->level);
1010 add_property_long(return_value, "code", error->code);
1011 add_property_long(return_value, "column", error->int2);
1012 if (error->message) {
1013 add_property_string(return_value, "message", error->message, 1);
1014 } else {
1015 add_property_stringl(return_value, "message", "", 0, 1);
1016 }
1017 if (error->file) {
1018 add_property_string(return_value, "file", error->file, 1);
1019 } else {
1020 add_property_stringl(return_value, "file", "", 0, 1);
1021 }
1022 add_property_long(return_value, "line", error->line);
1023 } else {
1024 RETURN_FALSE;
1025 }
1026 }
1027 /* }}} */
1028
1029 /* {{{ proto object libxml_get_errors()
1030 Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)1031 static PHP_FUNCTION(libxml_get_errors)
1032 {
1033
1034 xmlErrorPtr error;
1035
1036 if (array_init(return_value) == FAILURE) {
1037 RETURN_FALSE;
1038 }
1039
1040 if (LIBXML(error_list)) {
1041
1042 error = zend_llist_get_first(LIBXML(error_list));
1043
1044 while (error != NULL) {
1045 zval *z_error;
1046 MAKE_STD_ZVAL(z_error);
1047
1048 object_init_ex(z_error, libxmlerror_class_entry);
1049 add_property_long(z_error, "level", error->level);
1050 add_property_long(z_error, "code", error->code);
1051 add_property_long(z_error, "column", error->int2);
1052 if (error->message) {
1053 add_property_string(z_error, "message", error->message, 1);
1054 } else {
1055 add_property_stringl(z_error, "message", "", 0, 1);
1056 }
1057 if (error->file) {
1058 add_property_string(z_error, "file", error->file, 1);
1059 } else {
1060 add_property_stringl(z_error, "file", "", 0, 1);
1061 }
1062 add_property_long(z_error, "line", error->line);
1063 add_next_index_zval(return_value, z_error);
1064
1065 error = zend_llist_get_next(LIBXML(error_list));
1066 }
1067 }
1068 }
1069 /* }}} */
1070
1071 /* {{{ proto void libxml_clear_errors()
1072 Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)1073 static PHP_FUNCTION(libxml_clear_errors)
1074 {
1075 xmlResetLastError();
1076 if (LIBXML(error_list)) {
1077 zend_llist_clean(LIBXML(error_list));
1078 }
1079 }
1080 /* }}} */
1081
php_libxml_disable_entity_loader(zend_bool disable TSRMLS_DC)1082 PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable TSRMLS_DC)
1083 {
1084 zend_bool old = LIBXML(entity_loader_disabled);
1085
1086 LIBXML(entity_loader_disabled) = disable;
1087 return old;
1088 }
1089
1090 /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1091 Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)1092 static PHP_FUNCTION(libxml_disable_entity_loader)
1093 {
1094 zend_bool disable = 1;
1095
1096 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &disable) == FAILURE) {
1097 return;
1098 }
1099
1100 RETURN_BOOL(php_libxml_disable_entity_loader(disable TSRMLS_CC));
1101 }
1102 /* }}} */
1103
1104 /* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1105 Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)1106 static PHP_FUNCTION(libxml_set_external_entity_loader)
1107 {
1108 zend_fcall_info fci;
1109 zend_fcall_info_cache fcc;
1110 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", &fci, &fcc)
1111 == FAILURE) {
1112 return;
1113 }
1114
1115 _php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
1116
1117 if (fci.size > 0) { /* argument not null */
1118 LIBXML(entity_loader).fci = fci;
1119 Z_ADDREF_P(fci.function_name);
1120 if (fci.object_ptr != NULL) {
1121 Z_ADDREF_P(fci.object_ptr);
1122 }
1123 LIBXML(entity_loader).fcc = fcc;
1124 }
1125
1126 RETURN_TRUE;
1127 }
1128 /* }}} */
1129
1130 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)1131 int php_libxml_xmlCheckUTF8(const unsigned char *s)
1132 {
1133 int i;
1134 unsigned char c;
1135
1136 for (i = 0; (c = s[i++]);) {
1137 if ((c & 0x80) == 0) {
1138 } else if ((c & 0xe0) == 0xc0) {
1139 if ((s[i++] & 0xc0) != 0x80) {
1140 return 0;
1141 }
1142 } else if ((c & 0xf0) == 0xe0) {
1143 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1144 return 0;
1145 }
1146 } else if ((c & 0xf8) == 0xf0) {
1147 if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1148 return 0;
1149 }
1150 } else {
1151 return 0;
1152 }
1153 }
1154 return 1;
1155 }
1156
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)1157 int php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1158 {
1159 php_libxml_func_handler export_hnd;
1160
1161 /* Initialize in case this module hasnt been loaded yet */
1162 php_libxml_initialize();
1163 export_hnd.export_func = export_function;
1164
1165 return zend_hash_add(&php_libxml_exports, ce->name, ce->name_length + 1, &export_hnd, sizeof(export_hnd), NULL);
1166 }
1167
php_libxml_import_node(zval * object TSRMLS_DC)1168 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object TSRMLS_DC)
1169 {
1170 zend_class_entry *ce = NULL;
1171 xmlNodePtr node = NULL;
1172 php_libxml_func_handler *export_hnd;
1173
1174 if (object->type == IS_OBJECT) {
1175 ce = Z_OBJCE_P(object);
1176 while (ce->parent != NULL) {
1177 ce = ce->parent;
1178 }
1179 if (zend_hash_find(&php_libxml_exports, ce->name, ce->name_length + 1, (void **) &export_hnd) == SUCCESS) {
1180 node = export_hnd->export_func(object TSRMLS_CC);
1181 }
1182 }
1183 return node;
1184 }
1185
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data TSRMLS_DC)1186 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data TSRMLS_DC)
1187 {
1188 int ret_refcount = -1;
1189
1190 if (object != NULL && node != NULL) {
1191 if (object->node != NULL) {
1192 if (object->node->node == node) {
1193 return object->node->refcount;
1194 } else {
1195 php_libxml_decrement_node_ptr(object TSRMLS_CC);
1196 }
1197 }
1198 if (node->_private != NULL) {
1199 object->node = node->_private;
1200 ret_refcount = ++object->node->refcount;
1201 /* Only dom uses _private */
1202 if (object->node->_private == NULL) {
1203 object->node->_private = private_data;
1204 }
1205 } else {
1206 ret_refcount = 1;
1207 object->node = emalloc(sizeof(php_libxml_node_ptr));
1208 object->node->node = node;
1209 object->node->refcount = 1;
1210 object->node->_private = private_data;
1211 node->_private = object->node;
1212 }
1213 }
1214
1215 return ret_refcount;
1216 }
1217
php_libxml_decrement_node_ptr(php_libxml_node_object * object TSRMLS_DC)1218 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object TSRMLS_DC)
1219 {
1220 int ret_refcount = -1;
1221 php_libxml_node_ptr *obj_node;
1222
1223 if (object != NULL && object->node != NULL) {
1224 obj_node = (php_libxml_node_ptr *) object->node;
1225 ret_refcount = --obj_node->refcount;
1226 if (ret_refcount == 0) {
1227 if (obj_node->node != NULL) {
1228 obj_node->node->_private = NULL;
1229 }
1230 efree(obj_node);
1231 }
1232 object->node = NULL;
1233 }
1234
1235 return ret_refcount;
1236 }
1237
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp TSRMLS_DC)1238 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp TSRMLS_DC)
1239 {
1240 int ret_refcount = -1;
1241
1242 if (object->document != NULL) {
1243 object->document->refcount++;
1244 ret_refcount = object->document->refcount;
1245 } else if (docp != NULL) {
1246 ret_refcount = 1;
1247 object->document = emalloc(sizeof(php_libxml_ref_obj));
1248 object->document->ptr = docp;
1249 object->document->refcount = ret_refcount;
1250 object->document->doc_props = NULL;
1251 }
1252
1253 return ret_refcount;
1254 }
1255
php_libxml_decrement_doc_ref(php_libxml_node_object * object TSRMLS_DC)1256 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object TSRMLS_DC)
1257 {
1258 int ret_refcount = -1;
1259
1260 if (object != NULL && object->document != NULL) {
1261 ret_refcount = --object->document->refcount;
1262 if (ret_refcount == 0) {
1263 if (object->document->ptr != NULL) {
1264 xmlFreeDoc((xmlDoc *) object->document->ptr);
1265 }
1266 if (object->document->doc_props != NULL) {
1267 if (object->document->doc_props->classmap) {
1268 zend_hash_destroy(object->document->doc_props->classmap);
1269 FREE_HASHTABLE(object->document->doc_props->classmap);
1270 }
1271 efree(object->document->doc_props);
1272 }
1273 efree(object->document);
1274 object->document = NULL;
1275 }
1276 }
1277
1278 return ret_refcount;
1279 }
1280
php_libxml_node_free_resource(xmlNodePtr node TSRMLS_DC)1281 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node TSRMLS_DC)
1282 {
1283 if (!node) {
1284 return;
1285 }
1286
1287 switch (node->type) {
1288 case XML_DOCUMENT_NODE:
1289 case XML_HTML_DOCUMENT_NODE:
1290 break;
1291 default:
1292 if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1293 php_libxml_node_free_list((xmlNodePtr) node->children TSRMLS_CC);
1294 switch (node->type) {
1295 /* Skip property freeing for the following types */
1296 case XML_ATTRIBUTE_DECL:
1297 case XML_DTD_NODE:
1298 case XML_DOCUMENT_TYPE_NODE:
1299 case XML_ENTITY_DECL:
1300 case XML_ATTRIBUTE_NODE:
1301 case XML_NAMESPACE_DECL:
1302 case XML_TEXT_NODE:
1303 break;
1304 default:
1305 php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
1306 }
1307 if (php_libxml_unregister_node(node TSRMLS_CC) == 0) {
1308 node->doc = NULL;
1309 }
1310 php_libxml_node_free(node);
1311 } else {
1312 php_libxml_unregister_node(node TSRMLS_CC);
1313 }
1314 }
1315 }
1316
php_libxml_node_decrement_resource(php_libxml_node_object * object TSRMLS_DC)1317 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object TSRMLS_DC)
1318 {
1319 int ret_refcount = -1;
1320 xmlNodePtr nodep;
1321 php_libxml_node_ptr *obj_node;
1322
1323 if (object != NULL && object->node != NULL) {
1324 obj_node = (php_libxml_node_ptr *) object->node;
1325 nodep = object->node->node;
1326 ret_refcount = php_libxml_decrement_node_ptr(object TSRMLS_CC);
1327 if (ret_refcount == 0) {
1328 php_libxml_node_free_resource(nodep TSRMLS_CC);
1329 } else {
1330 if (obj_node && object == obj_node->_private) {
1331 obj_node->_private = NULL;
1332 }
1333 }
1334 }
1335 if (object != NULL && object->document != NULL) {
1336 /* Safe to call as if the resource were freed then doc pointer is NULL */
1337 php_libxml_decrement_doc_ref(object TSRMLS_CC);
1338 }
1339 }
1340 /* }}} */
1341
1342 #ifdef PHP_WIN32
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1343 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1344 {
1345 return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1346 }
1347 #endif
1348
1349 #endif
1350
1351 /*
1352 * Local variables:
1353 * tab-width: 4
1354 * c-basic-offset: 4
1355 * End:
1356 * vim600: sw=4 ts=4 fdm=marker
1357 * vim<600: sw=4 ts=4
1358 */
1359