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