Continued a bit:
[mailer.git] / inc / expression-functions.php
1 <?php
2 /************************************************************************
3  * Mailer v0.2.1-FINAL                                Start: 04/12/2009 *
4  * ===================                          Last change: 04/12/2009 *
5  *                                                                      *
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                      *
16  *                                                                      *
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.                                  *
21  *                                                                      *
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.                         *
26  *                                                                      *
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,               *
30  * MA  02110-1301  USA                                                  *
31  ************************************************************************/
32
33 // Some security stuff...
34 if (!defined('__SECURITY')) {
35         die();
36 } // END - if
37
38 // Private function to replace the code
39 function replaceExpressionCode ($data, $replacer) {
40         // Replace the code
41         // @TODO is escapeQuotes() enough for strings with single/double quotes?
42         return str_replace($data['matches'][0][$data['key']], $replacer, escapeQuotes($data['code']));
43 }
44
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']]);
50
51         // Is there cache?
52         if (!isset($GLOBALS['expression_function_available'][$entry])) {
53                 // Init function name
54                 $functionName = 'get';
55
56                 // Explode it in an array
57                 foreach (explode('_', $entry) as $piece) {
58                         // Add non-empty parts
59                         if (!empty($piece)) {
60                                 // Add it
61                                 $functionName .= capitalizeUnderscoreString($piece);
62                         } // END - if
63                 } // END - foreach
64
65                 // Is that function there?
66                 if (function_exists($functionName)) {
67                         // Cache it all
68                         $GLOBALS['expression_function_name'][$entry]      = $functionName;
69                         $GLOBALS['expression_function_available'][$entry] = TRUE;
70                 } else {
71                         // Not avaiable
72                         logDebugMessage(__FUNCTION__, __LINE__, 'Expression function ' . $functionName . ' not found. Please consider adding it to improve execution speed.');
73
74                         // And cache it
75                         $GLOBALS['expression_function_available'][$entry] = FALSE;
76                 }
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.');
80         }
81
82         // Return cache
83         return $GLOBALS['expression_function_available'][$entry];
84 }
85
86 // Getter for above expression function
87 function getExpressionFunction ($data) {
88         // Get the enty we need
89         $entry = trim($data['matches'][4][$data['key']]);
90
91         // Return it
92         return $GLOBALS['expression_function_name'][$entry];
93 }
94
95 // Expression call-back function for getCode() calls
96 function doExpressionCode ($data) {
97         // Replace the code
98         $code = str_replace($data['matches'][0][$data['key']], "{DQUOTE} . getCode('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}", $data['code']);
99
100         // Return replaced code
101         return $code;
102 }
103
104 // Expression call-back function for URLs (example: {%url=foo.php?bar=bar%})
105 function doExpressionUrl ($data) {
106         // Is there JS-mode?
107         if ($data['callback'] == 'js') {
108                 // Switch to it
109                 $data['output_mode'] = '1';
110         } // END - if
111
112         // Handle an URL here
113         $replacer = "{DQUOTE} . encodeUrl('" . $data['matches'][4][$data['key']] . "', " . $data['output_mode'] . ') . {DQUOTE}';
114
115         // Debug log
116         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer . ',callback=' . $data['callback'] . ',output_mode=' . $data['output_mode']);
117
118         // Replace it
119         $code = replaceExpressionCode($data, $replacer);
120
121         // Return replaced code
122         return $code;
123 }
124
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'] . "')))";
129
130         // Generate replacer
131         $replacer = '{DQUOTE} . call_user_func(' . $functionName . ') . {DQUOTE}';
132
133         // Replace it
134         $code = replaceExpressionCode($data, $replacer);
135
136         // Return replaced code
137         return $code;
138 }
139
140 // Expression call-back function for getting extension data
141 function doExpressionExt ($data) {
142         // Not installed is default
143         $replacer = 'false';
144
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']);
149
150                 // Construct call of the function
151                 $replacer = "{DQUOTE} . call_user_func_array('" . $functionName . "', array('" . $data['matches'][4][$data['key']] . "', TRUE)) . {DQUOTE}";
152         } // END - if
153
154         // Generate replacer
155         $replacer = sprintf('&amp;ext=%s&amp;ver=%s', $data['matches'][4][$data['key']], $replacer);
156
157         // Replace it and insert parameter for GET request
158         $code = replaceExpressionCode($data, $replacer);
159
160         // Debug message
161         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'code=' . $code . ',replacer=' . $replacer . ',key=' . $data['key'] . ',callback=' . $data['callback']);
162
163         // Return replaced code
164         return $code;
165 }
166
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)) {
172                 // Then use it
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}';
177         } else {
178                 // Default replacer is the config value itself
179                 $replacer = '{DQUOTE}  . ' . $data['callback'] . '(getConfig(' . chr(39) . $data['matches'][4][$data['key']] . chr(39) . ')) . {DQUOTE}';
180         }
181
182         // Replace the config entry
183         $code = replaceExpressionCode($data, $replacer);
184
185         // Return replaced code
186         return $code;
187 }
188
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'];
193
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')) {
200                         // No value is set
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}';
205                 } else {
206                         // Some string/integer value is set
207                         $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . "('" . $data['value'] . "'))) . {DQUOTE}";
208                 }
209         } // END - if
210
211         // Replace the config entry
212         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer);
213         $code = replaceExpressionCode($data, $replacer);
214
215         // Return replaced code
216         return $code;
217 }
218
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}";
223
224         // Run the filter and insert result
225         $code = replaceExpressionCode($data, $replacer);
226
227         // Return replaced code
228         return $code;
229 }
230
231 // Expression call-back function for validator links
232 function doExpressionValidatorLinks ($data) {
233         // Default is nothing
234         $replacer = '';
235
236         // Get the code from data array for replacement/pipe-through
237         $code = $data['code'];
238
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));
243         } // END - if
244
245         // Replace the code
246         $code = replaceExpressionCode($data, $replacer);
247
248         // Return the (maybe) replaced code
249         return $code;
250 }
251
252 // Expression call-back for dynamic messages
253 function doExpressionMessage ($data) {
254         // Debug message
255         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'callback=' . $data['callback'] . ',extra_func=' . $data['extra_func'] . ',value=' . $data['value']);
256
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}";
264         } else {
265                 // Regular message
266                 $replacer = "{DQUOTE} . getMessage('" . $data['callback'] . "') . {DQUOTE}";
267         }
268
269         // Replace the code
270         $code = replaceExpressionCode($data, $replacer);
271
272         // Return the (maybe) replaced code
273         return $code;
274 }
275
276 // Expression call-back for template functions
277 function doExpressionTemplate ($data) {
278         // Construct call-back function name
279         $callbackFunction = 'doTemplate' . ucfirst($data['callback']);
280
281         // Init replacer
282         $replacer = '<!-- [' . __FUNCTION__ . ':' . __LINE__.'] Call-back function ' . $callbackFunction  . ' does not exist. //-->';
283
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()) {
291                         // Log it here
292                         logDebugMessage(__FUNCTION__, __LINE__, 'Call-back function ' . $callbackFunction . ' does not exist.');
293                 } // END - if
294         } else {
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';
298
299                 // Is 'value' set?
300                 if (!empty($data['value'])) {
301                         // Then include it as well
302                         $replacer .= ', ' . chr(39) . $data['value'] . chr(39);
303                 } // END - if
304
305                 // Replacer is ready
306                 $replacer .= ') . {DQUOTE}';
307         }
308
309         // Replace the code
310         $code = replaceExpressionCode($data, $replacer);
311
312         // Return the (maybe) replaced code
313         return $code;
314 }
315
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}';
321
322         // Replace the code
323         $code = replaceExpressionCode($data, $replacer);
324
325         // Return the (maybe) replaced code
326         return $code;
327 }
328
329 // Expression call-back for GET request
330 function doExpressionGet ($data) {
331         // Construct the replacer:
332         // - GET request element
333         $replacer = '{%pipe,getRequestElement';
334
335         // Add more call-back functions?
336         if (!empty($data['callback'])) {
337                 // - Okay, add them
338                 $replacer .= ',' . $data['callback'];
339         } // END - if
340
341         // - Finalize replacer
342         $replacer .= '=' . $data['value'] . '%}';
343
344         // Replace the code
345         $code = replaceExpressionCode($data, $replacer);
346
347         // Return the (maybe) replaced code
348         return $code;
349 }
350
351 // Expression call-back for POST request
352 function doExpressionPost ($data) {
353         // Construct the replacer:
354         // - POST request element
355         $replacer = '{%pipe,postRequestElement';
356
357         // Add more call-back functions?
358         if (!empty($data['callback'])) {
359                 // - Okay, add them
360                 $replacer .= ',' . $data['callback'];
361         } // END - if
362
363         // - Finalize replacer
364         $replacer .= '=' . $data['value'] . '%}';
365
366         // Replace the code
367         $code = replaceExpressionCode($data, $replacer);
368
369         // Return the (maybe) replaced code
370         return $code;
371 }
372
373 // Expression call-back for session data
374 function doExpressionSession ($data) {
375         // Construct the replacer:
376         // - Session element
377         $replacer = '{%pipe,getSession';
378
379         // Add more call-back functions?
380         if (!empty($data['callback'])) {
381                 // - Okay, add them
382                 $replacer .= ',' . $data['callback'];
383         } // END - if
384
385         // - Finalize replacer
386         $replacer .= '=' . $data['value'] . '%}';
387
388         // Debug message
389         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',replacer=' . $replacer);
390
391         // Replace the code
392         $code = replaceExpressionCode($data, $replacer);
393
394         // Return the (maybe) replaced code
395         return $code;
396 }
397
398 /**
399  * Expression call-back for session piplining, this means:
400  *
401  * 1) Read session data
402  * 2) Wrap the raw data into {%pipe,fooFunction=$rawValue%}
403  */
404 function doExpressionSessionPipe ($data) {
405         // Get the session data
406         $rawValue = getSession($data['value']);
407
408         // Construct the replacer
409         $replacer = '{%pipe,' . $data['callback'] . '=' . $rawValue . '%}';
410
411         // Debug message
412         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',rawValue=' . $rawValue . ',replacer=' . $replacer);
413
414         // Replace the code
415         $code = replaceExpressionCode($data, $replacer);
416
417         // Return the (maybe) replaced code
418         return $code;
419 }
420
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']      = '';
429
430         // Check which method/target is set
431         foreach (array('callback', 'extra_func', 'extra_func2') as $key) {
432                 // Make lower-case
433                 $value = strtolower($data[$key]);
434
435                 // Is formMethodPost set?
436                 if ($value == 'formmethodpost') {
437                         // Use it
438                         $data['__form_method'] = 'post';
439                 } elseif (($value == 'formmethodpost') && (!isSpider()) && (!isValidSession())) {
440                         // Then expand 'value' with session id
441                         if (strpos($data['value'], '?') !== FALSE) {
442                                 // '?' is set
443                                 $data['value'] .= '&amp';
444                         } else {
445                                 // Is not set
446                                 $data['value'] .= '?';
447                         }
448
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') {
458                         // Form id found
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) . '%}';
463                 }
464         } // END - foreach
465
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'];
472                 } // END - if
473                 $data['__replacer'] .= '%}\"';
474         } // END - if
475
476         // Add rest elements
477         foreach (array('method', 'target', 'name', 'id') as $key) {
478                 $data['__replacer'] .= ' ' . $key . '=\"' . $data['__form_' . $key] . '\"';
479         } // END - foreach
480
481         // Close the tag here (don't move it below the next filter)
482         $data['__replacer'] .= '>' . PHP_EOL;
483
484         /*
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.
488          */
489         $data = runFilterChain('open_form_fields', $data);
490
491         // Replace the code
492         $code = replaceExpressionCode($data, $data['__replacer']);
493
494         // Return the (maybe) replaced code
495         return $code;
496 }
497
498 // Expression call-back to close form tags
499 function doExpressionFormClose ($data) {
500         // Initial replacer is really easy ...
501         $data['__replacer'] = '</form>' . PHP_EOL;
502
503         /*
504          * Call a filter chain to allow more hidden fields being added at the end
505          * of the form.
506          */
507         $data = runFilterChain('close_form_fields', $data);
508
509         // Replace the code
510         $code = replaceExpressionCode($data, $data['__replacer']);
511
512         // Return the (maybe) replaced code
513         return $code;
514 }
515
516 // Expression call-back to handle jquery inclusion
517 function doExpressionJquery ($data) {
518         // Default is compressed
519         $jquery = 'jquery';
520         $data['output_mode'] = '';
521
522         // Is there JS-mode?
523         if ($data['callback'] == 'js') {
524                 // Switch to it
525                 $data['output_mode'] = ',js';
526         } // END - if
527
528         // Is there a value?
529         if ($data['callback'] == 'js') {
530                 // Then load special library
531                 $jquery .= '-' . $data['value'];
532         } // END - if
533
534         // Is debug mode enabled?
535         if ((isGetRequestElementSet('jquery')) || (isSessionVariableSet('jquery'))) {
536                 // Then use uncompressed
537                 $jquery .= '.uncompressed';
538
539                 // Remember it in session
540                 setSession('jquery', '1');
541         } // END - if
542
543         // Add {%url%} around it
544         $replacer = '{%url' . $data['output_mode'] . '=js/' . $jquery . '.js?dummy=1%}';
545
546         // Replace the code
547         $code = replaceExpressionCode($data, $replacer);
548
549         // Return the (maybe) replaced code
550         return $code;
551 }
552
553 // [EOF]
554 ?>