1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Christian Stocker <chregu@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "php_xsl.h"
25 #include "php_xsl_arginfo.h"
26
27 zend_class_entry *xsl_xsltprocessor_class_entry;
28 static zend_object_handlers xsl_object_handlers;
29
30 static const zend_module_dep xsl_deps[] = {
31 ZEND_MOD_REQUIRED("libxml")
32 ZEND_MOD_REQUIRED("dom")
33 ZEND_MOD_END
34 };
35
36 /* {{{ xsl_module_entry */
37 zend_module_entry xsl_module_entry = {
38 STANDARD_MODULE_HEADER_EX, NULL,
39 xsl_deps,
40 "xsl",
41 NULL,
42 PHP_MINIT(xsl),
43 PHP_MSHUTDOWN(xsl),
44 NULL,
45 NULL,
46 PHP_MINFO(xsl),
47 PHP_XSL_VERSION,
48 STANDARD_MODULE_PROPERTIES
49 };
50 /* }}} */
51
52 #ifdef COMPILE_DL_XSL
ZEND_GET_MODULE(xsl)53 ZEND_GET_MODULE(xsl)
54 #endif
55
56 static HashTable *xsl_objects_get_gc(zend_object *object, zval **table, int *n)
57 {
58 xsl_object *intern = php_xsl_fetch_object(object);
59 return php_dom_xpath_callbacks_get_gc_for_whole_object(&intern->xpath_callbacks, object, table, n);
60 }
61
xsl_free_sheet(xsl_object * intern)62 void xsl_free_sheet(xsl_object *intern)
63 {
64 if (intern->ptr) {
65 xsltStylesheetPtr sheet = intern->ptr;
66
67 /* Free wrapper */
68 if (sheet->_private != NULL) {
69 sheet->_private = NULL;
70 }
71
72 xsltFreeStylesheet(sheet);
73 intern->ptr = NULL;
74 }
75 }
76
77 /* {{{ xsl_objects_free_storage */
xsl_objects_free_storage(zend_object * object)78 void xsl_objects_free_storage(zend_object *object)
79 {
80 xsl_object *intern = php_xsl_fetch_object(object);
81
82 zend_object_std_dtor(&intern->std);
83
84 if (intern->parameter) {
85 zend_hash_destroy(intern->parameter);
86 FREE_HASHTABLE(intern->parameter);
87 }
88
89 php_dom_xpath_callbacks_dtor(&intern->xpath_callbacks);
90
91 xsl_free_sheet(intern);
92
93 if (intern->doc) {
94 php_libxml_decrement_doc_ref(intern->doc);
95 efree(intern->doc);
96 }
97
98 if (intern->sheet_ref_obj) {
99 php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj);
100 }
101
102 if (intern->profiling) {
103 efree(intern->profiling);
104 }
105 }
106 /* }}} */
107
108 /* {{{ xsl_objects_new */
xsl_objects_new(zend_class_entry * class_type)109 zend_object *xsl_objects_new(zend_class_entry *class_type)
110 {
111 xsl_object *intern;
112
113 intern = zend_object_alloc(sizeof(xsl_object), class_type);
114 intern->securityPrefs = XSL_SECPREF_DEFAULT;
115
116 zend_object_std_init(&intern->std, class_type);
117 object_properties_init(&intern->std, class_type);
118 intern->parameter = zend_new_array(0);
119 php_dom_xpath_callbacks_ctor(&intern->xpath_callbacks);
120
121 /* Default initialize properties that could not be default initialized at the stub because they depend on library
122 * configuration parameters. */
123 ZVAL_LONG(xsl_prop_max_template_depth(&intern->std), xsltMaxDepth);
124 ZVAL_LONG(xsl_prop_max_template_vars(&intern->std), xsltMaxVars);
125
126 return &intern->std;
127 }
128 /* }}} */
129
130 #if ZEND_DEBUG
131 # define XSL_DEFINE_PROP_ACCESSOR(c_name, php_name, prop_index) \
132 zval *xsl_prop_##c_name(zend_object *object) \
133 { \
134 zend_string *prop_name = ZSTR_INIT_LITERAL(php_name, false); \
135 const zend_property_info *prop_info = zend_get_property_info(xsl_xsltprocessor_class_entry, prop_name, 0); \
136 zend_string_release_ex(prop_name, false); \
137 ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_index); \
138 return OBJ_PROP_NUM(object, prop_index); \
139 }
140 #else
141 # define XSL_DEFINE_PROP_ACCESSOR(c_name, php_name, prop_index) \
142 zval *xsl_prop_##c_name(zend_object *object) \
143 { \
144 return OBJ_PROP_NUM(object, prop_index); \
145 }
146 #endif
147
148 XSL_DEFINE_PROP_ACCESSOR(max_template_depth, "maxTemplateDepth", 2)
149 XSL_DEFINE_PROP_ACCESSOR(max_template_vars, "maxTemplateVars", 3)
150
xsl_objects_write_property_with_validation(zend_object * object,zend_string * member,zval * value,void ** cache_slot,zval * property)151 static zval *xsl_objects_write_property_with_validation(zend_object *object, zend_string *member, zval *value, void **cache_slot, zval *property)
152 {
153 /* Read old value so we can restore it if necessary. The value is not refcounted as its type is IS_LONG. */
154 ZEND_ASSERT(Z_TYPE_P(property) == IS_LONG);
155 zend_long old_property_value = Z_LVAL_P(property);
156
157 /* Write new property, which will also potentially perform coercions. */
158 zend_std_write_property(object, member, value, NULL);
159
160 /* Validate value *after* coercions have been performed, and restore the old value if necessary. */
161 if (UNEXPECTED(Z_LVAL_P(property) < 0)) {
162 Z_LVAL_P(property) = old_property_value;
163 zend_value_error("%s::$%s must be greater than or equal to 0", ZSTR_VAL(object->ce->name), ZSTR_VAL(member));
164 return &EG(error_zval);
165 }
166
167 return property;
168 }
169
xsl_objects_write_property(zend_object * object,zend_string * member,zval * value,void ** cache_slot)170 static zval *xsl_objects_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot)
171 {
172 /* Extra validation for maxTemplateDepth and maxTemplateVars */
173 if (zend_string_equals_literal(member, "maxTemplateDepth")) {
174 zval *property = xsl_prop_max_template_depth(object);
175 return xsl_objects_write_property_with_validation(object, member, value, cache_slot, property);
176 } else if (zend_string_equals_literal(member, "maxTemplateVars")) {
177 zval *property = xsl_prop_max_template_vars(object);
178 return xsl_objects_write_property_with_validation(object, member, value, cache_slot, property);
179 } else {
180 return zend_std_write_property(object, member, value, cache_slot);
181 }
182 }
183
xsl_is_validated_property(const zend_string * member)184 static bool xsl_is_validated_property(const zend_string *member)
185 {
186 return zend_string_equals_literal(member, "maxTemplateDepth") || zend_string_equals_literal(member, "maxTemplateVars");
187 }
188
xsl_objects_get_property_ptr_ptr(zend_object * object,zend_string * member,int type,void ** cache_slot)189 static zval *xsl_objects_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot)
190 {
191 if (xsl_is_validated_property(member)) {
192 return NULL;
193 }
194
195 return zend_std_get_property_ptr_ptr(object, member, type, cache_slot);
196 }
197
xsl_objects_read_property(zend_object * object,zend_string * member,int type,void ** cache_slot,zval * rv)198 static zval *xsl_objects_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv)
199 {
200 /* read handler is being called as a fallback after get_property_ptr_ptr returned NULL */
201 if (type != BP_VAR_IS && type != BP_VAR_R && xsl_is_validated_property(member)) {
202 zend_throw_error(NULL, "Indirect modification of %s::$%s is not allowed", ZSTR_VAL(object->ce->name), ZSTR_VAL(member));
203 return &EG(uninitialized_zval);
204 }
205
206 return zend_std_read_property(object, member, type, cache_slot, rv);
207 }
208
xsl_objects_unset_property(zend_object * object,zend_string * member,void ** cache_slot)209 static void xsl_objects_unset_property(zend_object *object, zend_string *member, void **cache_slot)
210 {
211 if (xsl_is_validated_property(member)) {
212 zend_throw_error(NULL, "Cannot unset %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(member));
213 return;
214 }
215
216 zend_std_unset_property(object, member, cache_slot);
217 }
218
219 /* Tries to output an error message where a part was replaced by another string.
220 * Returns true if the search string was found and the error message with replacement was outputted.
221 * Return false otherwise. */
xsl_try_output_replaced_error_message(void * ctx,const char * msg,va_list args,const char * search,size_t search_len,const char * replace)222 static bool xsl_try_output_replaced_error_message(
223 void *ctx,
224 const char *msg,
225 va_list args,
226 const char *search,
227 size_t search_len,
228 const char *replace
229 )
230 {
231 const char *msg_replace_location = strstr(msg, search);
232 if (msg_replace_location != NULL) {
233 php_libxml_ctx_error(ctx, "%.*s%s%s", (int) (msg_replace_location - msg), msg, replace, msg_replace_location + search_len);
234 return true;
235 }
236 return false;
237 }
238
239 /* Helper macro so the string length doesn't need to be passed separately.
240 * Only allows literal strings for `search` and `replace`. */
241 #define XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg, args, search, replace) \
242 xsl_try_output_replaced_error_message(ctx, msg, args, "" search, sizeof("" search) - 1, "" replace)
243
244 /* We want to output PHP-tailored error messages for some libxslt error messages, such that
245 * the errors refer to PHP properties instead of libxslt-specific fields. */
xsl_libxslt_error_handler(void * ctx,const char * msg,...)246 static void xsl_libxslt_error_handler(void *ctx, const char *msg, ...)
247 {
248 va_list args;
249 va_start(args, msg);
250
251 if (strcmp(msg, "%s") == 0) {
252 /* Adjust error message to be more descriptive */
253 const char *msg_arg = va_arg(args, const char *);
254 bool output = XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg_arg, args, "xsltMaxDepth (--maxdepth)", "$maxTemplateDepth")
255 || XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg_arg, args, "maxTemplateVars (--maxvars)", "$maxTemplateVars");
256
257 if (!output) {
258 php_libxml_ctx_error(ctx, "%s", msg_arg);
259 }
260 } else {
261 php_libxml_error_handler_va(PHP_LIBXML_ERROR, ctx, msg, args);
262 }
263
264 va_end(args);
265 }
266
267 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(xsl)268 PHP_MINIT_FUNCTION(xsl)
269 {
270 memcpy(&xsl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
271 xsl_object_handlers.offset = XtOffsetOf(xsl_object, std);
272 xsl_object_handlers.clone_obj = NULL;
273 xsl_object_handlers.free_obj = xsl_objects_free_storage;
274 xsl_object_handlers.get_gc = xsl_objects_get_gc;
275 xsl_object_handlers.write_property = xsl_objects_write_property;
276 xsl_object_handlers.get_property_ptr_ptr = xsl_objects_get_property_ptr_ptr;
277 xsl_object_handlers.read_property = xsl_objects_read_property;
278 xsl_object_handlers.unset_property = xsl_objects_unset_property;
279
280 xsl_xsltprocessor_class_entry = register_class_XSLTProcessor();
281 xsl_xsltprocessor_class_entry->create_object = xsl_objects_new;
282 xsl_xsltprocessor_class_entry->default_object_handlers = &xsl_object_handlers;
283
284 #ifdef HAVE_XSL_EXSLT
285 exsltRegisterAll();
286 #endif
287
288 xsltRegisterExtModuleFunction ((const xmlChar *) "functionString",
289 (const xmlChar *) "http://php.net/xsl",
290 xsl_ext_function_string_php);
291 xsltRegisterExtModuleFunction ((const xmlChar *) "function",
292 (const xmlChar *) "http://php.net/xsl",
293 xsl_ext_function_object_php);
294 xsltSetGenericErrorFunc(NULL, xsl_libxslt_error_handler);
295
296 register_php_xsl_symbols(module_number);
297
298 return SUCCESS;
299 }
300 /* }}} */
301
302 /* {{{ xsl_object_set_data */
xsl_object_set_data(void * obj,zval * wrapper)303 static void xsl_object_set_data(void *obj, zval *wrapper)
304 {
305 ((xsltStylesheetPtr) obj)->_private = wrapper;
306 }
307 /* }}} */
308
309 /* {{{ php_xsl_set_object */
php_xsl_set_object(zval * wrapper,void * obj)310 void php_xsl_set_object(zval *wrapper, void *obj)
311 {
312 xsl_object *object;
313
314 object = Z_XSL_P(wrapper);
315 object->ptr = obj;
316 xsl_object_set_data(obj, wrapper);
317 }
318 /* }}} */
319
320 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(xsl)321 PHP_MSHUTDOWN_FUNCTION(xsl)
322 {
323 xsltUnregisterExtModuleFunction ((const xmlChar *) "functionString",
324 (const xmlChar *) "http://php.net/xsl");
325 xsltUnregisterExtModuleFunction ((const xmlChar *) "function",
326 (const xmlChar *) "http://php.net/xsl");
327 xsltSetGenericErrorFunc(NULL, NULL);
328 xsltCleanupGlobals();
329
330 return SUCCESS;
331 }
332 /* }}} */
333
334 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(xsl)335 PHP_MINFO_FUNCTION(xsl)
336 {
337 php_info_print_table_start();
338 {
339 char buffer[128];
340 int major, minor, subminor;
341
342 php_info_print_table_row(2, "XSL", "enabled");
343 major = xsltLibxsltVersion/10000;
344 minor = (xsltLibxsltVersion - major * 10000) / 100;
345 subminor = (xsltLibxsltVersion - major * 10000 - minor * 100);
346 snprintf(buffer, 128, "%d.%d.%d", major, minor, subminor);
347 php_info_print_table_row(2, "libxslt Version", buffer);
348 major = xsltLibxmlVersion/10000;
349 minor = (xsltLibxmlVersion - major * 10000) / 100;
350 subminor = (xsltLibxmlVersion - major * 10000 - minor * 100);
351 snprintf(buffer, 128, "%d.%d.%d", major, minor, subminor);
352 php_info_print_table_row(2, "libxslt compiled against libxml Version", buffer);
353 }
354 #ifdef HAVE_XSL_EXSLT
355 php_info_print_table_row(2, "EXSLT", "enabled");
356 php_info_print_table_row(2, "libexslt Version", LIBEXSLT_DOTTED_VERSION);
357 #endif
358 php_info_print_table_end();
359 }
360 /* }}} */
361