2 /************************************************************************
3 * Mailer v0.2.1-FINAL Start: 04/12/2009 *
4 * =================== Last change: 04/12/2009 *
6 * -------------------------------------------------------------------- *
7 * File : expression-functions.php *
8 * -------------------------------------------------------------------- *
9 * Short description : Expression callback functions *
10 * -------------------------------------------------------------------- *
11 * Kurzbeschreibung : Expression-Callback-Funktionen *
12 * -------------------------------------------------------------------- *
13 * Copyright (c) 2003 - 2009 by Roland Haeder *
14 * Copyright (c) 2009 - 2016 by Mailer Developer Team *
15 * For more information visit: http://mxchange.org *
17 * This program is free software; you can redistribute it and/or modify *
18 * it under the terms of the GNU General Public License as published by *
19 * the Free Software Foundation; either version 2 of the License, or *
20 * (at your option) any later version. *
22 * This program is distributed in the hope that it will be useful, *
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
25 * GNU General Public License for more details. *
27 * You should have received a copy of the GNU General Public License *
28 * along with this program; if not, write to the Free Software *
29 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
31 ************************************************************************/
33 // Some security stuff...
34 if (!defined('__SECURITY')) {
38 // Private function to replace the code
39 function replaceExpressionCode ($data, $replacer) {
41 // @TODO is escapeQuotes() enough for strings with single/double quotes?
42 return str_replace($data['matches'][0][$data['key']], $replacer, escapeQuotes($data['code']));
45 // Private function to determine whether we have a special expression function avaible
46 // (mostly located in wrapper-functions.php)
47 function isExpressionFunctionAvaiable ($data) {
48 // Get the enty we need and trim it
49 $entry = trim($data['matches'][4][$data['key']]);
52 if (!isset($GLOBALS['expression_function_available'][$entry])) {
54 $functionName = 'get';
56 // Explode it in an array
57 foreach (explode('_', $entry) as $piece) {
58 // Add non-empty parts
61 $functionName .= capitalizeUnderscoreString($piece);
65 // Is that function there?
66 if (function_exists($functionName)) {
68 $GLOBALS['expression_function_name'][$entry] = $functionName;
69 $GLOBALS['expression_function_available'][$entry] = TRUE;
72 logDebugMessage(__FUNCTION__, __LINE__, 'Expression function ' . $functionName . ' not found. Please consider adding it to improve execution speed.');
75 $GLOBALS['expression_function_available'][$entry] = FALSE;
77 } elseif (($GLOBALS['expression_function_available'][$entry] == FALSE) && (isDebugModeEnabled())) {
78 // Debug message in debug mode
79 logDebugMessage(__FUNCTION__, __LINE__, 'Expression function for entry ' . $entry . ' requested but does not exist.');
83 return $GLOBALS['expression_function_available'][$entry];
86 // Getter for above expression function
87 function getExpressionFunction ($data) {
88 // Get the enty we need
89 $entry = trim($data['matches'][4][$data['key']]);
92 return $GLOBALS['expression_function_name'][$entry];
95 // Expression call-back function for getCode() calls
96 function doExpressionCode ($data) {
98 $code = str_replace($data['matches'][0][$data['key']], "{DQUOTE} . getCode('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}", $data['code']);
100 // Return replaced code
104 // Expression call-back function for URLs (example: {%url=foo.php?bar=bar%})
105 function doExpressionUrl ($data) {
107 if ($data['callback'] == 'js') {
109 $data['output_mode'] = '1';
112 // Handle an URL here
113 $replacer = "{DQUOTE} . encodeUrl('" . $data['matches'][4][$data['key']] . "', " . $data['output_mode'] . ') . {DQUOTE}';
116 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer . ',callback=' . $data['callback'] . ',output_mode=' . $data['output_mode']);
119 $code = replaceExpressionCode($data, $replacer);
121 // Return replaced code
125 // Expression call-back function for reading data from $_SERVER
126 function doExpressionServer ($data) {
127 // This will make 'foo_bar' to detectFooBar()
128 $functionName = "'detect' . implode('', array_map('firstCharUpperCase', explode('_', '" . $data['callback'] . "')))";
131 $replacer = '{DQUOTE} . call_user_func(' . $functionName . ') . {DQUOTE}';
134 $code = replaceExpressionCode($data, $replacer);
136 // Return replaced code
140 // Expression call-back function for getting extension data
141 function doExpressionExt ($data) {
142 // Not installed is default
145 // Is the extension installed?
146 if (isExtensionInstalled($data['matches'][4][$data['key']])) {
147 // Construct call-back function name
148 $functionName = 'getExtension' . capitalizeUnderscoreString($data['callback']);
150 // Construct call of the function
151 $replacer = "{DQUOTE} . call_user_func_array('" . $functionName . "', array('" . $data['matches'][4][$data['key']] . "', TRUE)) . {DQUOTE}";
155 $replacer = sprintf('&ext=%s&ver=%s', $data['matches'][4][$data['key']], $replacer);
157 // Replace it and insert parameter for GET request
158 $code = replaceExpressionCode($data, $replacer);
161 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'code=' . $code . ',replacer=' . $replacer . ',key=' . $data['key'] . ',callback=' . $data['callback']);
163 // Return replaced code
167 // Expression call-back function for getting configuration data
168 // @TODO FILTER_COMPILE_CONFIG does not handle call-back functions so we handle it here again
169 function doExpressionConfig ($data) {
170 // Is there a special expression function for it?
171 if (isExpressionFunctionAvaiable($data)) {
173 $replacer = '{DQUOTE} . ' . $data['callback'] . '(' . getExpressionFunction($data) . '()) . {DQUOTE}';
174 } elseif (!isConfigEntrySet($data['matches'][4][$data['key']])) {
175 // Config entry is not set
176 $replacer = '{DQUOTE} . ' . $data['callback'] . '(NULL) . {DQUOTE}';
178 // Default replacer is the config value itself
179 $replacer = '{DQUOTE} . ' . $data['callback'] . '(getConfig(' . chr(39) . $data['matches'][4][$data['key']] . chr(39) . ')) . {DQUOTE}';
182 // Replace the config entry
183 $code = replaceExpressionCode($data, $replacer);
185 // Return replaced code
189 // Expression call-back function for piping data through functions
190 function doExpressionPipe ($data) {
191 // We need callback and extra_func: callback is really the call-back function, extra_func is our value
192 $replacer = $data['extra_func'];
194 // Is there a call-back? Should always be there!
195 if (!empty($data['callback'])) {
196 //* DEBUG: */ if ($data['callback'] == 'getMemberId') die('<pre>'.encodeEntities(print_r($data, TRUE)).'</pre>');
197 // If the value is empty, we don't add it
198 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value[' . gettype($data['value']) . ']=' . $data['value']);
199 if ((empty($data['value'])) && ($data['value'] != '0')) {
201 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '())) . {DQUOTE}';
202 } elseif (isXmlTypeBool($data['value'])) {
203 // Boolean value detected
204 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '(' . $data['value'] . '))) . {DQUOTE}';
206 // Some string/integer value is set
207 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . "('" . $data['value'] . "'))) . {DQUOTE}";
211 // Replace the config entry
212 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer);
213 $code = replaceExpressionCode($data, $replacer);
215 // Return replaced code
219 // Expression call-back function for calling filters
220 function doExpressionFilter ($data) {
221 // Construct replacement
222 $replacer = "{DQUOTE} . runFilterChain('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}";
224 // Run the filter and insert result
225 $code = replaceExpressionCode($data, $replacer);
227 // Return replaced code
231 // Expression call-back function for validator links
232 function doExpressionValidatorLinks ($data) {
233 // Default is nothing
236 // Get the code from data array for replacement/pipe-through
237 $code = $data['code'];
239 // Should we generally include validator links?
240 if ((isExtensionInstalled('validator')) && (getConfig('enable_validator') == 'Y') && (!in_array(getModule(), array('admin', 'login')))) {
241 // Load the validator template
242 $replacer = escapeQuotes(loadTemplate('validator_links', TRUE));
246 $code = replaceExpressionCode($data, $replacer);
248 // Return the (maybe) replaced code
252 // Expression call-back for dynamic messages
253 function doExpressionMessage ($data) {
255 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'callback=' . $data['callback'] . ',extra_func=' . $data['extra_func'] . ',value=' . $data['value']);
257 // Message string replacement depends on if message is masked
258 if ((isMessageMasked($data['callback'], FALSE)) && ((!empty($data['extra_func'])) || ($data['extra_func'] == '0'))) {
259 // Message should be masked
260 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['extra_func'] . "') . {DQUOTE}";
261 } elseif (!empty($data['value'])) {
262 // value is set, so it is masked message
263 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['value'] . "') . {DQUOTE}";
266 $replacer = "{DQUOTE} . getMessage('" . $data['callback'] . "') . {DQUOTE}";
270 $code = replaceExpressionCode($data, $replacer);
272 // Return the (maybe) replaced code
276 // Expression call-back for template functions
277 function doExpressionTemplate ($data) {
278 // Construct call-back function name
279 $callbackFunction = 'doTemplate' . ucfirst($data['callback']);
282 $replacer = '<!-- [' . __FUNCTION__ . ':' . __LINE__.'] Call-back function ' . $callbackFunction . ' does not exist. //-->';
284 // Is the function there?
285 if (!isset($GLOBALS['current_template'])) {
286 // This is very bad and needs fixing
287 reportBug(__FUNCTION__, __LINE__, 'current_template in GLOBALS not set, callbackFunction=' . $callbackFunction . ',function_exists()=' . intval(function_exists($callbackFunction)));
288 } elseif (!function_exists($callbackFunction)) {
289 // Log missing function only in debug mode
290 if (isDebugModeEnabled()) {
292 logDebugMessage(__FUNCTION__, __LINE__, 'Call-back function ' . $callbackFunction . ' does not exist.');
295 // Do the replacement
296 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
297 $replacer = '{DQUOTE} . ' . $callbackFunction . '(' . chr(39) . $GLOBALS['current_template'] . chr(39) . ', TRUE';
300 if (!empty($data['value'])) {
301 // Then include it as well
302 $replacer .= ', ' . chr(39) . $data['value'] . chr(39);
306 $replacer .= ') . {DQUOTE}';
310 $code = replaceExpressionCode($data, $replacer);
312 // Return the (maybe) replaced code
316 // Expression call-back for math functions
317 function doExpressionMath ($data) {
318 // Do the replacement
319 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
320 $replacer = '{DQUOTE} . doCalculate' . $data['callback'] . '(' . $data['value'] . ') . {DQUOTE}';
323 $code = replaceExpressionCode($data, $replacer);
325 // Return the (maybe) replaced code
329 // Expression call-back for GET request
330 function doExpressionGet ($data) {
331 // Construct the replacer:
332 // - GET request element
333 $replacer = '{%pipe,getRequestElement';
335 // Add more call-back functions?
336 if (!empty($data['callback'])) {
338 $replacer .= ',' . $data['callback'];
341 // - Finalize replacer
342 $replacer .= '=' . $data['value'] . '%}';
345 $code = replaceExpressionCode($data, $replacer);
347 // Return the (maybe) replaced code
351 // Expression call-back for POST request
352 function doExpressionPost ($data) {
353 // Construct the replacer:
354 // - POST request element
355 $replacer = '{%pipe,postRequestElement';
357 // Add more call-back functions?
358 if (!empty($data['callback'])) {
360 $replacer .= ',' . $data['callback'];
363 // - Finalize replacer
364 $replacer .= '=' . $data['value'] . '%}';
367 $code = replaceExpressionCode($data, $replacer);
369 // Return the (maybe) replaced code
373 // Expression call-back for session data
374 function doExpressionSession ($data) {
375 // Construct the replacer:
377 $replacer = '{%pipe,getSession';
379 // Add more call-back functions?
380 if (!empty($data['callback'])) {
382 $replacer .= ',' . $data['callback'];
385 // - Finalize replacer
386 $replacer .= '=' . $data['value'] . '%}';
389 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',replacer=' . $replacer);
392 $code = replaceExpressionCode($data, $replacer);
394 // Return the (maybe) replaced code
399 * Expression call-back for session piplining, this means:
401 * 1) Read session data
402 * 2) Wrap the raw data into {%pipe,fooFunction=$rawValue%}
404 function doExpressionSessionPipe ($data) {
405 // Get the session data
406 $rawValue = getSession($data['value']);
408 // Construct the replacer
409 $replacer = '{%pipe,' . $data['callback'] . '=' . $rawValue . '%}';
412 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',rawValue=' . $rawValue . ',replacer=' . $replacer);
415 $code = replaceExpressionCode($data, $replacer);
417 // Return the (maybe) replaced code
421 // Expression call-back for formulars
422 function doExpressionForm ($data) {
423 // Default method is GET, target is _self
424 $data['__form_method'] = 'get';
425 $data['__form_target'] = '_self';
426 $data['__form_name'] = 'form';
427 $data['__form_id'] = 'form';
428 $data['__server'] = '';
430 // Check which method/target is set
431 foreach (array('callback', 'extra_func', 'extra_func2') as $key) {
433 $value = strtolower($data[$key]);
435 // Is formMethodPost set?
436 if ($value == 'formmethodpost') {
438 $data['__form_method'] = 'post';
439 } elseif (($value == 'formmethodpost') && (!isSpider()) && (!isValidSession())) {
440 // Then expand 'value' with session id
441 if (strpos($data['value'], '?') !== FALSE) {
443 $data['value'] .= '&';
446 $data['value'] .= '?';
449 // Append session's name and id
450 $data['value'] .= session_name() . '=' . session_id();
451 } elseif (substr($value, 0, 10) == 'formtarget') {
452 // Form target is found
453 $data['__form_target'] = substr($value, 10);
454 } elseif (substr($value, 0, 8) == 'formname') {
455 // Form name is found
456 $data['__form_name'] = substr($value, 8);
457 } elseif (substr($value, 0, 6) == 'formid') {
459 $data['__form_id'] = substr($value, 6);
460 } elseif (substr($value, 0, 6) == 'server') {
461 // {%server,foo%} found
462 $data['__server'] = '{%server,' . substr($value, 6) . '%}';
466 // Generate the replacement code which is the opening form tag
467 $data['__replacer'] = '<form accept-charset=\"UTF-8\"';
468 if (!empty($data['value'])) {
469 $data['__replacer'] .= ' action=\"{%url=' . $data['value'];
470 if (!empty($data['__server'])) {
471 $data['__replacer'] .= $data['__server'];
473 $data['__replacer'] .= '%}\"';
477 foreach (array('method', 'target', 'name', 'id') as $key) {
478 $data['__replacer'] .= ' ' . $key . '=\"' . $data['__form_' . $key] . '\"';
481 // Close the tag here (don't move it below the next filter)
482 $data['__replacer'] .= '>' . PHP_EOL;
485 * Call a filter chain to allow more hidden fields being added. You should
486 * not remove the > char from above line to add onsubmit="" or so. Instead
487 * you should better use jquery to accomplish the same.
489 $data = runFilterChain('open_form_fields', $data);
492 $code = replaceExpressionCode($data, $data['__replacer']);
494 // Return the (maybe) replaced code
498 // Expression call-back to close form tags
499 function doExpressionFormClose ($data) {
500 // Initial replacer is really easy ...
501 $data['__replacer'] = '</form>' . PHP_EOL;
504 * Call a filter chain to allow more hidden fields being added at the end
507 $data = runFilterChain('close_form_fields', $data);
510 $code = replaceExpressionCode($data, $data['__replacer']);
512 // Return the (maybe) replaced code
516 // Expression call-back to handle jquery inclusion
517 function doExpressionJquery ($data) {
518 // Default is uncompressed
519 $scriptName = 'jquery';
520 $data['output_mode'] = '';
523 if ($data['callback'] == 'js') {
525 $data['output_mode'] = ',js';
529 if (!empty($data['value'])) {
530 // Then load special library
531 $scriptName .= '-' . $data['value'];
534 // Is debug-mode enabled and parameter/session key 'jquery' given?
535 if ((!isDebugModeEnabled()) || ((!isGetRequestElementSet('jquery')) && (!isSessionVariableSet('jquery')))) {
536 // Productive systems want minimized version
537 $scriptName .= '.min';
539 // Debug version is uncompressed, remember it in session
540 setSession('jquery', '1');
543 // Add {%url%} around it
544 $replacer = sprintf('{%%url%s=js/jquery/%s.js?dummy=1%%}', $data['output_mode'], $scriptName);
547 $code = replaceExpressionCode($data, $replacer);
549 // Return the (maybe) replaced code
553 // Expression call-back to handle JavaScript inclusion
554 function doExpressionJs ($data) {
555 /* DEBUG-DIE: */ die(sprintf('[%s:%d]: data=<pre>%s</pre>', __FUNCTION__, __LINE__, print_r($data, TRUE)));