1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: libxml.c 282841 2009-06-26 20:27:44Z rrichards $ */
21 :
22 : #define IS_EXT_MODULE
23 :
24 : #ifdef HAVE_CONFIG_H
25 : #include "config.h"
26 : #endif
27 :
28 : #include "php.h"
29 :
30 : #define PHP_XML_INTERNAL
31 : #include "zend_variables.h"
32 : #include "ext/standard/php_string.h"
33 : #include "ext/standard/info.h"
34 : #include "ext/standard/file.h"
35 :
36 : #if HAVE_LIBXML
37 :
38 : #include <libxml/parser.h>
39 : #include <libxml/parserInternals.h>
40 : #include <libxml/tree.h>
41 : #include <libxml/uri.h>
42 : #include <libxml/xmlerror.h>
43 : #include <libxml/xmlsave.h>
44 : #ifdef LIBXML_SCHEMAS_ENABLED
45 : #include <libxml/relaxng.h>
46 : #endif
47 :
48 : #include "php_libxml.h"
49 :
50 : #define PHP_LIBXML_ERROR 0
51 : #define PHP_LIBXML_CTX_ERROR 1
52 : #define PHP_LIBXML_CTX_WARNING 2
53 :
54 : /* a true global for initialization */
55 : static int _php_libxml_initialized = 0;
56 :
57 : typedef struct _php_libxml_func_handler {
58 : php_libxml_export_node export_func;
59 : } php_libxml_func_handler;
60 :
61 : static HashTable php_libxml_exports;
62 :
63 : static ZEND_DECLARE_MODULE_GLOBALS(libxml)
64 : static PHP_GINIT_FUNCTION(libxml);
65 :
66 : static PHP_FUNCTION(libxml_set_streams_context);
67 : static PHP_FUNCTION(libxml_use_internal_errors);
68 : static PHP_FUNCTION(libxml_get_last_error);
69 : static PHP_FUNCTION(libxml_clear_errors);
70 : static PHP_FUNCTION(libxml_get_errors);
71 : static PHP_FUNCTION(libxml_disable_entity_loader);
72 :
73 : static zend_class_entry *libxmlerror_class_entry;
74 :
75 : /* {{{ dynamically loadable module stuff */
76 : #ifdef COMPILE_DL_LIBXML
77 : ZEND_GET_MODULE(libxml)
78 : #endif /* COMPILE_DL_LIBXML */
79 : /* }}} */
80 :
81 : /* {{{ function prototypes */
82 : static PHP_MINIT_FUNCTION(libxml);
83 : static PHP_RINIT_FUNCTION(libxml);
84 : static PHP_MSHUTDOWN_FUNCTION(libxml);
85 : static PHP_RSHUTDOWN_FUNCTION(libxml);
86 : static PHP_MINFO_FUNCTION(libxml);
87 :
88 : /* }}} */
89 :
90 : /* {{{ arginfo */
91 : static
92 : ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0)
93 : ZEND_ARG_INFO(0, context)
94 : ZEND_END_ARG_INFO()
95 :
96 : static
97 : ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0)
98 : ZEND_ARG_INFO(0, use_errors)
99 : ZEND_END_ARG_INFO()
100 :
101 : static
102 : ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0)
103 : ZEND_END_ARG_INFO()
104 :
105 : static
106 : ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0)
107 : ZEND_END_ARG_INFO()
108 :
109 : static
110 : ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0)
111 : ZEND_END_ARG_INFO()
112 :
113 : static
114 : ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0)
115 : ZEND_ARG_INFO(0, disable)
116 : ZEND_END_ARG_INFO()
117 :
118 : /* }}} */
119 :
120 : /* {{{ extension definition structures */
121 : static zend_function_entry libxml_functions[] = {
122 : PHP_FE(libxml_set_streams_context, arginfo_libxml_set_streams_context)
123 : PHP_FE(libxml_use_internal_errors, arginfo_libxml_use_internal_errors)
124 : PHP_FE(libxml_get_last_error, arginfo_libxml_get_last_error)
125 : PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors)
126 : PHP_FE(libxml_get_errors, arginfo_libxml_get_errors)
127 : PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader)
128 : {NULL, NULL, NULL}
129 : };
130 :
131 : zend_module_entry libxml_module_entry = {
132 : STANDARD_MODULE_HEADER,
133 : "libxml", /* extension name */
134 : libxml_functions, /* extension function list */
135 : PHP_MINIT(libxml), /* extension-wide startup function */
136 : PHP_MSHUTDOWN(libxml), /* extension-wide shutdown function */
137 : PHP_RINIT(libxml), /* per-request startup function */
138 : PHP_RSHUTDOWN(libxml), /* per-request shutdown function */
139 : PHP_MINFO(libxml), /* information function */
140 : NO_VERSION_YET,
141 : PHP_MODULE_GLOBALS(libxml), /* globals descriptor */
142 : PHP_GINIT(libxml), /* globals ctor */
143 : NULL, /* globals dtor */
144 : NULL, /* post deactivate */
145 : STANDARD_MODULE_PROPERTIES_EX
146 : };
147 :
148 : /* }}} */
149 :
150 : /* {{{ internal functions for interoperability */
151 : static int php_libxml_clear_object(php_libxml_node_object *object TSRMLS_DC)
152 7 : {
153 7 : if (object->properties) {
154 7 : object->properties = NULL;
155 : }
156 7 : php_libxml_decrement_node_ptr(object TSRMLS_CC);
157 7 : return php_libxml_decrement_doc_ref(object TSRMLS_CC);
158 : }
159 :
160 : static int php_libxml_unregister_node(xmlNodePtr nodep TSRMLS_DC)
161 201358 : {
162 : php_libxml_node_object *wrapper;
163 :
164 201358 : php_libxml_node_ptr *nodeptr = nodep->_private;
165 :
166 201358 : if (nodeptr != NULL) {
167 16 : wrapper = nodeptr->_private;
168 16 : if (wrapper) {
169 7 : php_libxml_clear_object(wrapper TSRMLS_CC);
170 : } else {
171 9 : if (nodeptr->node != NULL && nodeptr->node->type != XML_DOCUMENT_NODE) {
172 9 : nodeptr->node->_private = NULL;
173 : }
174 9 : nodeptr->node = NULL;
175 : }
176 : }
177 :
178 201358 : return -1;
179 : }
180 :
181 : static void php_libxml_node_free(xmlNodePtr node)
182 255 : {
183 255 : if(node) {
184 255 : if (node->_private != NULL) {
185 0 : ((php_libxml_node_ptr *) node->_private)->node = NULL;
186 : }
187 255 : switch (node->type) {
188 : case XML_ATTRIBUTE_NODE:
189 27 : xmlFreeProp((xmlAttrPtr) node);
190 27 : break;
191 : case XML_ENTITY_DECL:
192 : case XML_ELEMENT_DECL:
193 : case XML_ATTRIBUTE_DECL:
194 0 : break;
195 : case XML_NOTATION_NODE:
196 : /* These require special handling */
197 5 : if (node->name != NULL) {
198 5 : xmlFree((char *) node->name);
199 : }
200 5 : if (((xmlEntityPtr) node)->ExternalID != NULL) {
201 5 : xmlFree((char *) ((xmlEntityPtr) node)->ExternalID);
202 : }
203 5 : if (((xmlEntityPtr) node)->SystemID != NULL) {
204 4 : xmlFree((char *) ((xmlEntityPtr) node)->SystemID);
205 : }
206 5 : xmlFree(node);
207 5 : break;
208 : case XML_NAMESPACE_DECL:
209 0 : if (node->ns) {
210 0 : xmlFreeNs(node->ns);
211 0 : node->ns = NULL;
212 : }
213 0 : node->type = XML_ELEMENT_NODE;
214 : default:
215 223 : xmlFreeNode(node);
216 : }
217 : }
218 255 : }
219 :
220 : static void php_libxml_node_free_list(xmlNodePtr node TSRMLS_DC)
221 361 : {
222 : xmlNodePtr curnode;
223 :
224 361 : if (node != NULL) {
225 78 : curnode = node;
226 294 : while (curnode != NULL) {
227 138 : node = curnode;
228 138 : switch (node->type) {
229 : /* Skip property freeing for the following types */
230 : case XML_NOTATION_NODE:
231 0 : break;
232 : case XML_ENTITY_REF_NODE:
233 0 : php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
234 0 : break;
235 : case XML_ATTRIBUTE_NODE:
236 15 : if ((node->doc != NULL) && (((xmlAttrPtr) node)->atype == XML_ATTRIBUTE_ID)) {
237 1 : xmlRemoveID(node->doc, (xmlAttrPtr) node);
238 : }
239 : case XML_ATTRIBUTE_DECL:
240 : case XML_DTD_NODE:
241 : case XML_DOCUMENT_TYPE_NODE:
242 : case XML_ENTITY_DECL:
243 : case XML_NAMESPACE_DECL:
244 : case XML_TEXT_NODE:
245 106 : php_libxml_node_free_list(node->children TSRMLS_CC);
246 106 : break;
247 : default:
248 32 : php_libxml_node_free_list(node->children TSRMLS_CC);
249 32 : php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
250 : }
251 :
252 138 : curnode = node->next;
253 138 : xmlUnlinkNode(node);
254 138 : if (php_libxml_unregister_node(node TSRMLS_CC) == 0) {
255 0 : node->doc = NULL;
256 : }
257 138 : php_libxml_node_free(node);
258 : }
259 : }
260 361 : }
261 :
262 : /* }}} */
263 :
264 : /* {{{ startup, shutdown and info functions */
265 : static PHP_GINIT_FUNCTION(libxml)
266 13565 : {
267 13565 : libxml_globals->stream_context = NULL;
268 13565 : libxml_globals->error_buffer.c = NULL;
269 13565 : libxml_globals->error_list = NULL;
270 13565 : }
271 :
272 : /* Channel libxml file io layer through the PHP streams subsystem.
273 : * This allows use of ftps:// and https:// urls */
274 :
275 : static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only)
276 891 : {
277 : php_stream_statbuf ssbuf;
278 891 : php_stream_context *context = NULL;
279 891 : php_stream_wrapper *wrapper = NULL;
280 891 : char *resolved_path, *path_to_open = NULL;
281 891 : void *ret_val = NULL;
282 891 : int isescaped=0;
283 : xmlURI *uri;
284 :
285 : TSRMLS_FETCH();
286 :
287 891 : uri = xmlParseURI((xmlChar *)filename);
288 1770 : if (uri && (uri->scheme == NULL || (xmlStrncmp(uri->scheme, "file", 4) == 0))) {
289 879 : resolved_path = xmlURIUnescapeString(filename, 0, NULL);
290 879 : isescaped = 1;
291 : } else {
292 12 : resolved_path = (char *)filename;
293 : }
294 :
295 891 : if (uri) {
296 890 : xmlFreeURI(uri);
297 : }
298 :
299 891 : if (resolved_path == NULL) {
300 0 : return NULL;
301 : }
302 :
303 : /* logic copied from _php_stream_stat, but we only want to fail
304 : if the wrapper supports stat, otherwise, figure it out from
305 : the open. This logic is only to support hiding warnings
306 : that the streams layer puts out at times, but for libxml we
307 : may try to open files that don't exist, but it is not a failure
308 : in xml processing (eg. DTD files) */
309 891 : wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
310 891 : if (wrapper && read_only && wrapper->wops->url_stat) {
311 865 : if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL TSRMLS_CC) == -1) {
312 2 : if (isescaped) {
313 1 : xmlFree(resolved_path);
314 : }
315 2 : return NULL;
316 : }
317 : }
318 :
319 889 : if (LIBXML(stream_context)) {
320 8 : context = zend_fetch_resource(&LIBXML(stream_context) TSRMLS_CC, -1, "Stream-Context", NULL, 1, php_le_stream_context());
321 : }
322 :
323 889 : ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL, context);
324 889 : if (isescaped) {
325 878 : xmlFree(resolved_path);
326 : }
327 889 : return ret_val;
328 : }
329 :
330 : static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
331 876 : {
332 876 : return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
333 : }
334 :
335 : static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
336 15 : {
337 15 : return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
338 : }
339 :
340 : static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
341 3720 : {
342 : TSRMLS_FETCH();
343 3720 : return php_stream_read((php_stream*)context, buffer, len);
344 : }
345 :
346 : static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
347 24 : {
348 : TSRMLS_FETCH();
349 24 : return php_stream_write((php_stream*)context, buffer, len);
350 : }
351 :
352 : static int php_libxml_streams_IO_close(void *context)
353 889 : {
354 : TSRMLS_FETCH();
355 889 : return php_stream_close((php_stream*)context);
356 : }
357 :
358 : static xmlParserInputBufferPtr
359 : php_libxml_input_buffer_noload(const char *URI, xmlCharEncoding enc)
360 0 : {
361 0 : return NULL;
362 : }
363 :
364 : static xmlParserInputBufferPtr
365 : php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
366 876 : {
367 : xmlParserInputBufferPtr ret;
368 876 : void *context = NULL;
369 :
370 876 : if (URI == NULL)
371 0 : return(NULL);
372 :
373 876 : context = php_libxml_streams_IO_open_read_wrapper(URI);
374 :
375 876 : if (context == NULL) {
376 2 : return(NULL);
377 : }
378 :
379 : /* Allocate the Input buffer front-end. */
380 874 : ret = xmlAllocParserInputBuffer(enc);
381 874 : if (ret != NULL) {
382 874 : ret->context = context;
383 874 : ret->readcallback = php_libxml_streams_IO_read;
384 874 : ret->closecallback = php_libxml_streams_IO_close;
385 : } else
386 0 : php_libxml_streams_IO_close(context);
387 :
388 874 : return(ret);
389 : }
390 :
391 : static xmlOutputBufferPtr
392 : php_libxml_output_buffer_create_filename(const char *URI,
393 : xmlCharEncodingHandlerPtr encoder,
394 : int compression ATTRIBUTE_UNUSED)
395 15 : {
396 : xmlOutputBufferPtr ret;
397 : xmlURIPtr puri;
398 15 : void *context = NULL;
399 15 : char *unescaped = NULL;
400 :
401 15 : if (URI == NULL)
402 0 : return(NULL);
403 :
404 15 : puri = xmlParseURI(URI);
405 15 : if (puri != NULL) {
406 15 : if (puri->scheme != NULL)
407 1 : unescaped = xmlURIUnescapeString(URI, 0, NULL);
408 15 : xmlFreeURI(puri);
409 : }
410 :
411 15 : if (unescaped != NULL) {
412 1 : context = php_libxml_streams_IO_open_write_wrapper(unescaped);
413 1 : xmlFree(unescaped);
414 : }
415 :
416 : /* try with a non-escaped URI this may be a strange filename */
417 15 : if (context == NULL) {
418 14 : context = php_libxml_streams_IO_open_write_wrapper(URI);
419 : }
420 :
421 15 : if (context == NULL) {
422 0 : return(NULL);
423 : }
424 :
425 : /* Allocate the Output buffer front-end. */
426 15 : ret = xmlAllocOutputBuffer(encoder);
427 15 : if (ret != NULL) {
428 15 : ret->context = context;
429 15 : ret->writecallback = php_libxml_streams_IO_write;
430 15 : ret->closecallback = php_libxml_streams_IO_close;
431 : }
432 :
433 15 : return(ret);
434 : }
435 :
436 : static int _php_libxml_free_error(xmlErrorPtr error)
437 2 : {
438 : /* This will free the libxml alloc'd memory */
439 2 : xmlResetError(error);
440 2 : return 1;
441 : }
442 :
443 : static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
444 2 : {
445 : xmlError error_copy;
446 : int ret;
447 :
448 : TSRMLS_FETCH();
449 :
450 2 : memset(&error_copy, 0, sizeof(xmlError));
451 :
452 2 : if (error) {
453 2 : ret = xmlCopyError(error, &error_copy);
454 : } else {
455 0 : error_copy.domain = 0;
456 0 : error_copy.code = XML_ERR_INTERNAL_ERROR;
457 0 : error_copy.level = XML_ERR_ERROR;
458 0 : error_copy.line = 0;
459 0 : error_copy.node = NULL;
460 0 : error_copy.int1 = 0;
461 0 : error_copy.int2 = 0;
462 0 : error_copy.ctxt = NULL;
463 0 : error_copy.message = xmlStrdup(msg);
464 0 : error_copy.file = NULL;
465 0 : error_copy.str1 = NULL;
466 0 : error_copy.str2 = NULL;
467 0 : error_copy.str3 = NULL;
468 0 : ret = 0;
469 : }
470 :
471 2 : if (ret == 0) {
472 2 : zend_llist_add_element(LIBXML(error_list), &error_copy);
473 : }
474 2 : }
475 :
476 : static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg TSRMLS_DC)
477 2 : {
478 : xmlParserCtxtPtr parser;
479 :
480 2 : parser = (xmlParserCtxtPtr) ctx;
481 :
482 2 : if (parser != NULL && parser->input != NULL) {
483 2 : if (parser->input->filename) {
484 0 : php_error_docref(NULL TSRMLS_CC, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
485 : } else {
486 2 : php_error_docref(NULL TSRMLS_CC, level, "%s in Entity, line: %d", msg, parser->input->line);
487 : }
488 : }
489 2 : }
490 :
491 : void php_libxml_issue_error(int level, const char *msg TSRMLS_DC)
492 9 : {
493 9 : if (LIBXML(error_list)) {
494 0 : _php_list_set_error_structure(NULL, msg);
495 : } else {
496 9 : php_error_docref(NULL TSRMLS_CC, level, "%s", msg);
497 : }
498 9 : }
499 :
500 : static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
501 50 : {
502 : char *buf;
503 50 : int len, len_iter, output = 0;
504 :
505 : TSRMLS_FETCH();
506 :
507 50 : len = vspprintf(&buf, 0, *msg, ap);
508 50 : len_iter = len;
509 :
510 : /* remove any trailing \n */
511 131 : while (len_iter && buf[--len_iter] == '\n') {
512 31 : buf[len_iter] = '\0';
513 31 : output = 1;
514 : }
515 :
516 50 : smart_str_appendl(&LIBXML(error_buffer), buf, len);
517 :
518 50 : efree(buf);
519 :
520 50 : if (output == 1) {
521 31 : if (LIBXML(error_list)) {
522 0 : _php_list_set_error_structure(NULL, LIBXML(error_buffer).c);
523 : } else {
524 31 : switch (error_type) {
525 : case PHP_LIBXML_CTX_ERROR:
526 2 : php_libxml_ctx_error_level(E_WARNING, ctx, LIBXML(error_buffer).c TSRMLS_CC);
527 2 : break;
528 : case PHP_LIBXML_CTX_WARNING:
529 0 : php_libxml_ctx_error_level(E_NOTICE, ctx, LIBXML(error_buffer).c TSRMLS_CC);
530 0 : break;
531 : default:
532 29 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", LIBXML(error_buffer).c);
533 : }
534 : }
535 31 : smart_str_free(&LIBXML(error_buffer));
536 : }
537 50 : }
538 :
539 : void php_libxml_ctx_error(void *ctx, const char *msg, ...)
540 2 : {
541 : va_list args;
542 2 : va_start(args, msg);
543 2 : php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
544 2 : va_end(args);
545 2 : }
546 :
547 : void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
548 0 : {
549 : va_list args;
550 0 : va_start(args, msg);
551 0 : php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
552 0 : va_end(args);
553 0 : }
554 :
555 : PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
556 2 : {
557 2 : _php_list_set_error_structure(error, NULL);
558 :
559 : return;
560 : }
561 :
562 : PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
563 48 : {
564 : va_list args;
565 48 : va_start(args, msg);
566 48 : php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
567 48 : va_end(args);
568 48 : }
569 :
570 :
571 : PHP_LIBXML_API void php_libxml_initialize(void)
572 40695 : {
573 40695 : if (!_php_libxml_initialized) {
574 : /* we should be the only one's to ever init!! */
575 13565 : xmlInitParser();
576 :
577 13565 : zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
578 :
579 13565 : _php_libxml_initialized = 1;
580 : }
581 40695 : }
582 :
583 : PHP_LIBXML_API void php_libxml_shutdown(void)
584 13597 : {
585 13597 : if (_php_libxml_initialized) {
586 : #if defined(LIBXML_SCHEMAS_ENABLED)
587 13597 : xmlRelaxNGCleanupTypes();
588 : #endif
589 13597 : xmlCleanupParser();
590 13597 : zend_hash_destroy(&php_libxml_exports);
591 13597 : _php_libxml_initialized = 0;
592 : }
593 13597 : }
594 :
595 : PHP_LIBXML_API zval *php_libxml_switch_context(zval *context TSRMLS_DC)
596 4 : {
597 : zval *oldcontext;
598 :
599 4 : oldcontext = LIBXML(stream_context);
600 4 : LIBXML(stream_context) = context;
601 4 : return oldcontext;
602 :
603 : }
604 :
605 : static PHP_MINIT_FUNCTION(libxml)
606 13565 : {
607 : zend_class_entry ce;
608 :
609 13565 : php_libxml_initialize();
610 :
611 13565 : REGISTER_LONG_CONSTANT("LIBXML_VERSION", LIBXML_VERSION, CONST_CS | CONST_PERSISTENT);
612 13565 : REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION", LIBXML_DOTTED_VERSION, CONST_CS | CONST_PERSISTENT);
613 :
614 : /* For use with loading xml */
615 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOENT", XML_PARSE_NOENT, CONST_CS | CONST_PERSISTENT);
616 13565 : REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD", XML_PARSE_DTDLOAD, CONST_CS | CONST_PERSISTENT);
617 13565 : REGISTER_LONG_CONSTANT("LIBXML_DTDATTR", XML_PARSE_DTDATTR, CONST_CS | CONST_PERSISTENT);
618 13565 : REGISTER_LONG_CONSTANT("LIBXML_DTDVALID", XML_PARSE_DTDVALID, CONST_CS | CONST_PERSISTENT);
619 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOERROR", XML_PARSE_NOERROR, CONST_CS | CONST_PERSISTENT);
620 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOWARNING", XML_PARSE_NOWARNING, CONST_CS | CONST_PERSISTENT);
621 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS", XML_PARSE_NOBLANKS, CONST_CS | CONST_PERSISTENT);
622 13565 : REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE", XML_PARSE_XINCLUDE, CONST_CS | CONST_PERSISTENT);
623 13565 : REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN", XML_PARSE_NSCLEAN, CONST_CS | CONST_PERSISTENT);
624 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOCDATA", XML_PARSE_NOCDATA, CONST_CS | CONST_PERSISTENT);
625 13565 : REGISTER_LONG_CONSTANT("LIBXML_NONET", XML_PARSE_NONET, CONST_CS | CONST_PERSISTENT);
626 : #if LIBXML_VERSION >= 20621
627 13565 : REGISTER_LONG_CONSTANT("LIBXML_COMPACT", XML_PARSE_COMPACT, CONST_CS | CONST_PERSISTENT);
628 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL", XML_SAVE_NO_DECL, CONST_CS | CONST_PERSISTENT);
629 : #endif
630 13565 : REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG", LIBXML_SAVE_NOEMPTYTAG, CONST_CS | CONST_PERSISTENT);
631 :
632 : /* Error levels */
633 13565 : REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE", XML_ERR_NONE, CONST_CS | CONST_PERSISTENT);
634 13565 : REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING", XML_ERR_WARNING, CONST_CS | CONST_PERSISTENT);
635 13565 : REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR", XML_ERR_ERROR, CONST_CS | CONST_PERSISTENT);
636 13565 : REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL", XML_ERR_FATAL, CONST_CS | CONST_PERSISTENT);
637 :
638 13565 : INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
639 13565 : libxmlerror_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
640 :
641 13565 : return SUCCESS;
642 : }
643 :
644 :
645 : static PHP_RINIT_FUNCTION(libxml)
646 13551 : {
647 : /* report errors via handler rather than stderr */
648 13551 : xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
649 13551 : xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
650 13551 : xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
651 13551 : return SUCCESS;
652 : }
653 :
654 :
655 : static PHP_MSHUTDOWN_FUNCTION(libxml)
656 13597 : {
657 13597 : php_libxml_shutdown();
658 :
659 13597 : return SUCCESS;
660 : }
661 :
662 :
663 : static PHP_RSHUTDOWN_FUNCTION(libxml)
664 13584 : {
665 : /* reset libxml generic error handling */
666 13584 : xmlSetGenericErrorFunc(NULL, NULL);
667 13584 : xmlSetStructuredErrorFunc(NULL, NULL);
668 :
669 13584 : xmlParserInputBufferCreateFilenameDefault(NULL);
670 13584 : xmlOutputBufferCreateFilenameDefault(NULL);
671 :
672 13584 : if (LIBXML(stream_context)) {
673 1 : zval_ptr_dtor(&LIBXML(stream_context));
674 1 : LIBXML(stream_context) = NULL;
675 : }
676 13584 : smart_str_free(&LIBXML(error_buffer));
677 13584 : if (LIBXML(error_list)) {
678 2 : zend_llist_destroy(LIBXML(error_list));
679 2 : efree(LIBXML(error_list));
680 2 : LIBXML(error_list) = NULL;
681 : }
682 13584 : xmlResetLastError();
683 :
684 13584 : return SUCCESS;
685 : }
686 :
687 :
688 : static PHP_MINFO_FUNCTION(libxml)
689 6 : {
690 6 : php_info_print_table_start();
691 6 : php_info_print_table_row(2, "libXML support", "active");
692 6 : php_info_print_table_row(2, "libXML Version", LIBXML_DOTTED_VERSION);
693 6 : php_info_print_table_row(2, "libXML streams", "enabled");
694 6 : php_info_print_table_end();
695 6 : }
696 : /* }}} */
697 :
698 : /* {{{ proto void libxml_set_streams_context(resource streams_context)
699 : Set the streams context for the next libxml document load or write */
700 : static PHP_FUNCTION(libxml_set_streams_context)
701 8 : {
702 : zval *arg;
703 :
704 8 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
705 0 : return;
706 : }
707 8 : if (LIBXML(stream_context)) {
708 7 : zval_ptr_dtor(&LIBXML(stream_context));
709 7 : LIBXML(stream_context) = NULL;
710 : }
711 8 : ZVAL_ADDREF(arg);
712 8 : LIBXML(stream_context) = arg;
713 : }
714 : /* }}} */
715 :
716 : /* {{{ proto void libxml_use_internal_errors([boolean use_errors])
717 : Disable libxml errors and allow user to fetch error information as needed */
718 : static PHP_FUNCTION(libxml_use_internal_errors)
719 7 : {
720 : xmlStructuredErrorFunc current_handler;
721 7 : zend_bool use_errors=0, retval;
722 :
723 7 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &use_errors) == FAILURE) {
724 1 : return;
725 : }
726 :
727 6 : current_handler = xmlStructuredError;
728 8 : if (current_handler && current_handler == php_libxml_structured_error_handler) {
729 2 : retval = 1;
730 : } else {
731 4 : retval = 0;
732 : }
733 :
734 6 : if (ZEND_NUM_ARGS() == 0) {
735 1 : RETURN_BOOL(retval);
736 : }
737 :
738 5 : if (use_errors == 0) {
739 2 : xmlSetStructuredErrorFunc(NULL, NULL);
740 2 : if (LIBXML(error_list)) {
741 1 : zend_llist_destroy(LIBXML(error_list));
742 1 : efree(LIBXML(error_list));
743 1 : LIBXML(error_list) = NULL;
744 : }
745 : } else {
746 3 : xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
747 3 : if (LIBXML(error_list) == NULL) {
748 3 : LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
749 3 : zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
750 : }
751 : }
752 5 : RETURN_BOOL(retval);
753 : }
754 : /* }}} */
755 :
756 : /* {{{ proto object libxml_get_last_error()
757 : Retrieve last error from libxml */
758 : static PHP_FUNCTION(libxml_get_last_error)
759 2 : {
760 : xmlErrorPtr error;
761 :
762 2 : error = xmlGetLastError();
763 :
764 2 : if (error) {
765 1 : object_init_ex(return_value, libxmlerror_class_entry);
766 1 : add_property_long(return_value, "level", error->level);
767 1 : add_property_long(return_value, "code", error->code);
768 1 : add_property_long(return_value, "column", error->int2);
769 1 : if (error->message) {
770 1 : add_property_string(return_value, "message", error->message, 1);
771 : } else {
772 0 : add_property_stringl(return_value, "message", "", 0, 1);
773 : }
774 1 : if (error->file) {
775 0 : add_property_string(return_value, "file", error->file, 1);
776 : } else {
777 1 : add_property_stringl(return_value, "file", "", 0, 1);
778 : }
779 1 : add_property_long(return_value, "line", error->line);
780 : } else {
781 1 : RETURN_FALSE;
782 : }
783 : }
784 : /* }}} */
785 :
786 : /* {{{ proto object libxml_get_errors()
787 : Retrieve array of errors */
788 : static PHP_FUNCTION(libxml_get_errors)
789 2 : {
790 :
791 : xmlErrorPtr error;
792 :
793 2 : if (array_init(return_value) == FAILURE) {
794 0 : RETURN_FALSE;
795 : }
796 :
797 2 : if (LIBXML(error_list)) {
798 :
799 2 : error = zend_llist_get_first(LIBXML(error_list));
800 :
801 5 : while (error != NULL) {
802 : zval *z_error;
803 1 : MAKE_STD_ZVAL(z_error);
804 :
805 1 : object_init_ex(z_error, libxmlerror_class_entry);
806 1 : add_property_long(z_error, "level", error->level);
807 1 : add_property_long(z_error, "code", error->code);
808 1 : add_property_long(z_error, "column", error->int2);
809 1 : if (error->message) {
810 1 : add_property_string(z_error, "message", error->message, 1);
811 : } else {
812 0 : add_property_stringl(z_error, "message", "", 0, 1);
813 : }
814 1 : if (error->file) {
815 0 : add_property_string(z_error, "file", error->file, 1);
816 : } else {
817 1 : add_property_stringl(z_error, "file", "", 0, 1);
818 : }
819 1 : add_property_long(z_error, "line", error->line);
820 1 : add_next_index_zval(return_value, z_error);
821 :
822 1 : error = zend_llist_get_next(LIBXML(error_list));
823 : }
824 : }
825 : }
826 : /* }}} */
827 :
828 : /* {{{ proto void libxml_clear_errors()
829 : Clear last error from libxml */
830 : static PHP_FUNCTION(libxml_clear_errors)
831 1 : {
832 1 : xmlResetLastError();
833 1 : if (LIBXML(error_list)) {
834 1 : zend_llist_clean(LIBXML(error_list));
835 : }
836 1 : }
837 : /* }}} */
838 :
839 : /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
840 : Disable/Enable ability to load external entities */
841 : static PHP_FUNCTION(libxml_disable_entity_loader)
842 0 : {
843 0 : zend_bool disable = 1;
844 : xmlParserInputBufferCreateFilenameFunc old;
845 :
846 0 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &disable) == FAILURE) {
847 0 : return;
848 : }
849 :
850 0 : if (disable == 0) {
851 0 : old = xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
852 : } else {
853 0 : old = xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_noload);
854 : }
855 :
856 0 : if (old == php_libxml_input_buffer_noload) {
857 0 : RETURN_TRUE;
858 : }
859 :
860 0 : RETURN_FALSE;
861 : }
862 : /* }}} */
863 :
864 : /* {{{ Common functions shared by extensions */
865 : int php_libxml_xmlCheckUTF8(const unsigned char *s)
866 780 : {
867 : int i;
868 : unsigned char c;
869 :
870 9123 : for (i = 0; (c = s[i++]);) {
871 7564 : if ((c & 0x80) == 0) {
872 157 : } else if ((c & 0xe0) == 0xc0) {
873 156 : if ((s[i++] & 0xc0) != 0x80) {
874 0 : return 0;
875 : }
876 1 : } else if ((c & 0xf0) == 0xe0) {
877 0 : if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
878 0 : return 0;
879 : }
880 1 : } else if ((c & 0xf8) == 0xf0) {
881 0 : if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
882 0 : return 0;
883 : }
884 : } else {
885 1 : return 0;
886 : }
887 : }
888 779 : return 1;
889 : }
890 :
891 : int php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
892 27130 : {
893 : php_libxml_func_handler export_hnd;
894 :
895 : /* Initialize in case this module hasnt been loaded yet */
896 27130 : php_libxml_initialize();
897 27130 : export_hnd.export_func = export_function;
898 :
899 27130 : return zend_hash_add(&php_libxml_exports, ce->name, ce->name_length + 1, &export_hnd, sizeof(export_hnd), NULL);
900 : }
901 :
902 : PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object TSRMLS_DC)
903 59 : {
904 59 : zend_class_entry *ce = NULL;
905 59 : xmlNodePtr node = NULL;
906 : php_libxml_func_handler *export_hnd;
907 :
908 59 : if (object->type == IS_OBJECT) {
909 59 : ce = Z_OBJCE_P(object);
910 176 : while (ce->parent != NULL) {
911 58 : ce = ce->parent;
912 : }
913 59 : if (zend_hash_find(&php_libxml_exports, ce->name, ce->name_length + 1, (void **) &export_hnd) == SUCCESS) {
914 59 : node = export_hnd->export_func(object TSRMLS_CC);
915 : }
916 : }
917 59 : return node;
918 : }
919 :
920 : PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data TSRMLS_DC)
921 402223 : {
922 402223 : int ret_refcount = -1;
923 :
924 402223 : if (object != NULL && node != NULL) {
925 402223 : if (object->node != NULL) {
926 0 : if (object->node->node == node) {
927 0 : return object->node->refcount;
928 : } else {
929 0 : php_libxml_decrement_node_ptr(object TSRMLS_CC);
930 : }
931 : }
932 402223 : if (node->_private != NULL) {
933 200643 : object->node = node->_private;
934 200643 : ret_refcount = ++object->node->refcount;
935 : /* Only dom uses _private */
936 200643 : if (object->node->_private == NULL) {
937 200643 : object->node->_private = private_data;
938 : }
939 : } else {
940 201580 : ret_refcount = 1;
941 201580 : object->node = emalloc(sizeof(php_libxml_node_ptr));
942 201580 : object->node->node = node;
943 201580 : object->node->refcount = 1;
944 201580 : object->node->_private = private_data;
945 201580 : node->_private = object->node;
946 : }
947 : }
948 :
949 402223 : return ret_refcount;
950 : }
951 :
952 : PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object TSRMLS_DC)
953 402223 : {
954 402223 : int ret_refcount = -1;
955 : php_libxml_node_ptr *obj_node;
956 :
957 402223 : if (object != NULL && object->node != NULL) {
958 402223 : obj_node = (php_libxml_node_ptr *) object->node;
959 402223 : ret_refcount = --obj_node->refcount;
960 402223 : if (ret_refcount == 0) {
961 201580 : if (obj_node->node != NULL) {
962 201571 : obj_node->node->_private = NULL;
963 : }
964 201580 : efree(obj_node);
965 : }
966 402223 : object->node = NULL;
967 : }
968 :
969 402223 : return ret_refcount;
970 : }
971 :
972 : PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp TSRMLS_DC)
973 1000 : {
974 1000 : int ret_refcount = -1;
975 :
976 1000 : if (object->document != NULL) {
977 496 : object->document->refcount++;
978 496 : ret_refcount = object->document->refcount;
979 504 : } else if (docp != NULL) {
980 504 : ret_refcount = 1;
981 504 : object->document = emalloc(sizeof(php_libxml_ref_obj));
982 504 : object->document->ptr = docp;
983 504 : object->document->refcount = ret_refcount;
984 504 : object->document->doc_props = NULL;
985 : }
986 :
987 1000 : return ret_refcount;
988 : }
989 :
990 : PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object TSRMLS_DC)
991 402371 : {
992 402371 : int ret_refcount = -1;
993 :
994 402371 : if (object != NULL && object->document != NULL) {
995 402234 : ret_refcount = --object->document->refcount;
996 402234 : if (ret_refcount == 0) {
997 504 : if (object->document->ptr != NULL) {
998 504 : xmlFreeDoc((xmlDoc *) object->document->ptr);
999 : }
1000 504 : if (object->document->doc_props != NULL) {
1001 220 : if (object->document->doc_props->classmap) {
1002 1 : zend_hash_destroy(object->document->doc_props->classmap);
1003 1 : FREE_HASHTABLE(object->document->doc_props->classmap);
1004 : }
1005 220 : efree(object->document->doc_props);
1006 : }
1007 504 : efree(object->document);
1008 504 : object->document = NULL;
1009 : }
1010 : }
1011 :
1012 402371 : return ret_refcount;
1013 : }
1014 :
1015 : void php_libxml_node_free_resource(xmlNodePtr node TSRMLS_DC)
1016 201229 : {
1017 201229 : if (!node) {
1018 9 : return;
1019 : }
1020 :
1021 201220 : switch (node->type) {
1022 : case XML_DOCUMENT_NODE:
1023 : case XML_HTML_DOCUMENT_NODE:
1024 0 : break;
1025 : default:
1026 201337 : if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1027 117 : php_libxml_node_free_list((xmlNodePtr) node->children TSRMLS_CC);
1028 117 : switch (node->type) {
1029 : /* Skip property freeing for the following types */
1030 : case XML_ATTRIBUTE_DECL:
1031 : case XML_DTD_NODE:
1032 : case XML_DOCUMENT_TYPE_NODE:
1033 : case XML_ENTITY_DECL:
1034 : case XML_ATTRIBUTE_NODE:
1035 : case XML_NAMESPACE_DECL:
1036 : case XML_TEXT_NODE:
1037 43 : break;
1038 : default:
1039 74 : php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
1040 : }
1041 117 : if (php_libxml_unregister_node(node TSRMLS_CC) == 0) {
1042 0 : node->doc = NULL;
1043 : }
1044 117 : php_libxml_node_free(node);
1045 : } else {
1046 201103 : php_libxml_unregister_node(node TSRMLS_CC);
1047 : }
1048 : }
1049 : }
1050 :
1051 : void php_libxml_node_decrement_resource(php_libxml_node_object *object TSRMLS_DC)
1052 401835 : {
1053 401835 : int ret_refcount = -1;
1054 : xmlNodePtr nodep;
1055 : php_libxml_node_ptr *obj_node;
1056 :
1057 401835 : if (object != NULL && object->node != NULL) {
1058 401835 : obj_node = (php_libxml_node_ptr *) object->node;
1059 401835 : nodep = object->node->node;
1060 401835 : ret_refcount = php_libxml_decrement_node_ptr(object TSRMLS_CC);
1061 401835 : if (ret_refcount == 0) {
1062 201192 : php_libxml_node_free_resource(nodep TSRMLS_CC);
1063 : } else {
1064 200643 : if (obj_node && object == obj_node->_private) {
1065 1 : obj_node->_private = NULL;
1066 : }
1067 : }
1068 : }
1069 401835 : if (object != NULL && object->document != NULL) {
1070 : /* Safe to call as if the resource were freed then doc pointer is NULL */
1071 401814 : php_libxml_decrement_doc_ref(object TSRMLS_CC);
1072 : }
1073 401835 : }
1074 : /* }}} */
1075 :
1076 : #ifdef PHP_WIN32
1077 : PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1078 : {
1079 : return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1080 : }
1081 : #endif
1082 :
1083 : #endif
1084 :
1085 : /*
1086 : * Local variables:
1087 : * tab-width: 4
1088 : * c-basic-offset: 4
1089 : * End:
1090 : * vim600: sw=4 ts=4 fdm=marker
1091 : * vim<600: sw=4 ts=4
1092 : */
|