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