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);
326 loadIncludeOnce('inc/math-functions.php');
328 // Return the (maybe) replaced code
332 // Expression call-back for GET request
333 function doExpressionGet ($data) {
334 // Construct the replacer:
335 // - GET request element
336 $replacer = '{%pipe,getRequestElement';
338 // Add more call-back functions?
339 if (!empty($data['callback'])) {
341 $replacer .= ',' . $data['callback'];
344 // - Finalize replacer
345 $replacer .= '=' . $data['value'] . '%}';
348 $code = replaceExpressionCode($data, $replacer);
350 // Return the (maybe) replaced code
354 // Expression call-back for POST request
355 function doExpressionPost ($data) {
356 // Construct the replacer:
357 // - POST request element
358 $replacer = '{%pipe,postRequestElement';
360 // Add more call-back functions?
361 if (!empty($data['callback'])) {
363 $replacer .= ',' . $data['callback'];
366 // - Finalize replacer
367 $replacer .= '=' . $data['value'] . '%}';
370 $code = replaceExpressionCode($data, $replacer);
372 // Return the (maybe) replaced code
376 // Expression call-back for session data
377 function doExpressionSession ($data) {
378 // Construct the replacer:
380 $replacer = '{%pipe,getSession';
382 // Add more call-back functions?
383 if (!empty($data['callback'])) {
385 $replacer .= ',' . $data['callback'];
388 // - Finalize replacer
389 $replacer .= '=' . $data['value'] . '%}';
392 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',replacer=' . $replacer);
395 $code = replaceExpressionCode($data, $replacer);
397 // Return the (maybe) replaced code
402 * Expression call-back for session piplining, this means:
404 * 1) Read session data
405 * 2) Wrap the raw data into {%pipe,fooFunction=$rawValue%}
407 function doExpressionSessionPipe ($data) {
408 // Get the session data
409 $rawValue = getSession($data['value']);
411 // Construct the replacer
412 $replacer = '{%pipe,' . $data['callback'] . '=' . $rawValue . '%}';
415 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',rawValue=' . $rawValue . ',replacer=' . $replacer);
418 $code = replaceExpressionCode($data, $replacer);
420 // Return the (maybe) replaced code
424 // Expression call-back for formulars
425 function doExpressionForm ($data) {
426 // Default method is GET, target is _self
427 $data['__form_method'] = 'get';
428 $data['__form_target'] = '_self';
429 $data['__form_name'] = 'form';
430 $data['__form_id'] = 'form';
431 $data['__server'] = '';
433 // Check which method/target is set
434 foreach (array('callback', 'extra_func', 'extra_func2') as $key) {
436 $value = strtolower($data[$key]);
438 // Is formMethodPost set?
439 if ($value == 'formmethodpost') {
441 $data['__form_method'] = 'post';
442 } elseif (($value == 'formmethodpost') && (!isSpider()) && (!isValidSession())) {
443 // Then expand 'value' with session id
444 if (strpos($data['value'], '?') !== FALSE) {
446 $data['value'] .= '&';
449 $data['value'] .= '?';
452 // Append session's name and id
453 $data['value'] .= session_name() . '=' . session_id();
454 } elseif (substr($value, 0, 10) == 'formtarget') {
455 // Form target is found
456 $data['__form_target'] = substr($value, 10);
457 } elseif (substr($value, 0, 8) == 'formname') {
458 // Form name is found
459 $data['__form_name'] = substr($value, 8);
460 } elseif (substr($value, 0, 6) == 'formid') {
462 $data['__form_id'] = substr($value, 6);
463 } elseif (substr($value, 0, 6) == 'server') {
464 // {%server,foo%} found
465 $data['__server'] = '{%server,' . substr($value, 6) . '%}';
469 // Generate the replacement code which is the opening form tag
470 $data['__replacer'] = '<form accept-charset=\"UTF-8\"';
471 if (!empty($data['value'])) {
472 $data['__replacer'] .= ' action=\"{%url=' . $data['value'];
473 if (!empty($data['__server'])) {
474 $data['__replacer'] .= $data['__server'];
476 $data['__replacer'] .= '%}\"';
480 foreach (array('method', 'target', 'name', 'id') as $key) {
481 $data['__replacer'] .= ' ' . $key . '=\"' . $data['__form_' . $key] . '\"';
484 // Close the tag here (don't move it below the next filter)
485 $data['__replacer'] .= '>' . PHP_EOL;
488 * Call a filter chain to allow more hidden fields being added. You should
489 * not remove the > char from above line to add onsubmit="" or so. Instead
490 * you should better use jquery to accomplish the same.
492 $data = runFilterChain('open_form_fields', $data);
495 $code = replaceExpressionCode($data, $data['__replacer']);
497 // Return the (maybe) replaced code
501 // Expression call-back to close form tags
502 function doExpressionFormClose ($data) {
503 // Initial replacer is really easy ...
504 $data['__replacer'] = '</form>' . PHP_EOL;
507 * Call a filter chain to allow more hidden fields being added at the end
510 $data = runFilterChain('close_form_fields', $data);
513 $code = replaceExpressionCode($data, $data['__replacer']);
515 // Return the (maybe) replaced code
519 // Expression call-back to handle jquery inclusion
520 function doExpressionJquery ($data) {
521 // Default is compressed
523 $data['output_mode'] = '';
526 if ($data['callback'] == 'js') {
528 $data['output_mode'] = ',js';
532 if ($data['callback'] == 'js') {
533 // Then load special library
534 $jquery .= '-' . $data['value'];
537 // Is debug mode enabled?
538 if ((isGetRequestElementSet('jquery')) || (isSessionVariableSet('jquery'))) {
539 // Then use uncompressed
540 $jquery .= '.uncompressed';
542 // Remember it in session
543 setSession('jquery', '1');
546 // Add {%url%} around it
547 $replacer = '{%url' . $data['output_mode'] . '=js/' . $jquery . '.js?dummy=1%}';
550 $code = replaceExpressionCode($data, $replacer);
552 // Return the (maybe) replaced code