Must be updated here as well :(
[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  * $Revision::                                                        $ *
14  * $Date::                                                            $ *
15  * $Tag:: 0.2.1-FINAL                                                 $ *
16  * $Author::                                                          $ *
17  * -------------------------------------------------------------------- *
18  * Copyright (c) 2003 - 2009 by Roland Haeder                           *
19  * Copyright (c) 2009 - 2013 by Mailer Developer Team                   *
20  * For more information visit: http://mxchange.org                      *
21  *                                                                      *
22  * This program is free software; you can redistribute it and/or modify *
23  * it under the terms of the GNU General Public License as published by *
24  * the Free Software Foundation; either version 2 of the License, or    *
25  * (at your option) any later version.                                  *
26  *                                                                      *
27  * This program is distributed in the hope that it will be useful,      *
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
30  * GNU General Public License for more details.                         *
31  *                                                                      *
32  * You should have received a copy of the GNU General Public License    *
33  * along with this program; if not, write to the Free Software          *
34  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,               *
35  * MA  02110-1301  USA                                                  *
36  ************************************************************************/
37
38 // Some security stuff...
39 if (!defined('__SECURITY')) {
40         die();
41 } // END - if
42
43 // Private function to replace the code
44 function replaceExpressionCode ($data, $replacer) {
45         // Replace the code
46         // @TODO is escapeQuotes() enough for strings with single/double quotes?
47         return str_replace($data['matches'][0][$data['key']], $replacer, escapeQuotes($data['code']));
48 }
49
50 // Private function to determine whether we have a special expression function avaible
51 // (mostly located in wrapper-functions.php)
52 function isExpressionFunctionAvaiable ($data) {
53         // Get the enty we need and trim it
54         $entry = trim($data['matches'][4][$data['key']]);
55
56         // Is there cache?
57         if (!isset($GLOBALS['expression_function_available'][$entry])) {
58                 // Init function name
59                 $functionName = 'get';
60
61                 // Explode it in an array
62                 foreach (explode('_', $entry) as $piece) {
63                         // Add non-empty parts
64                         if (!empty($piece)) {
65                                 // Add it
66                                 $functionName .= capitalizeUnderscoreString($piece);
67                         } // END - if
68                 } // END - foreach
69
70                 // Is that function there?
71                 if (function_exists($functionName)) {
72                         // Cache it all
73                         $GLOBALS['expression_function_name'][$entry]      = $functionName;
74                         $GLOBALS['expression_function_available'][$entry] = TRUE;
75                 } else {
76                         // Not avaiable
77                         logDebugMessage(__FUNCTION__, __LINE__, 'Expression function ' . $functionName . ' not found. Please consider adding it to improve execution speed.');
78
79                         // And cache it
80                         $GLOBALS['expression_function_available'][$entry] = FALSE;
81                 }
82         } elseif (($GLOBALS['expression_function_available'][$entry] == FALSE) && (isDebugModeEnabled())) {
83                 // Debug message in debug mode
84                 logDebugMessage(__FUNCTION__, __LINE__, 'Expression function for entry ' . $entry . ' requested but does not exist.');
85         }
86
87         // Return cache
88         return $GLOBALS['expression_function_available'][$entry];
89 }
90
91 // Getter for above expression function
92 function getExpressionFunction ($data) {
93         // Get the enty we need
94         $entry = trim($data['matches'][4][$data['key']]);
95
96         // Return it
97         return $GLOBALS['expression_function_name'][$entry];
98 }
99
100 // Expression call-back function for getCode() calls
101 function doExpressionCode ($data) {
102         // Replace the code
103         $code = str_replace($data['matches'][0][$data['key']], "{DQUOTE} . getCode('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}", $data['code']);
104
105         // Return replaced code
106         return $code;
107 }
108
109 // Expression call-back function for URLs (example: {%url=foo.php?bar=bar%})
110 function doExpressionUrl ($data) {
111         // Is there JS-mode?
112         if ($data['callback'] == 'js') {
113                 // Switch to it
114                 $data['output_mode'] = '1';
115         } // END - if
116
117         // Handle an URL here
118         $replacer = "{DQUOTE} . encodeUrl('" . $data['matches'][4][$data['key']] . "', " . $data['output_mode'] . ') . {DQUOTE}';
119
120         // Debug log
121         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer . ',callback=' . $data['callback'] . ',output_mode=' . $data['output_mode']);
122
123         // Replace it
124         $code = replaceExpressionCode($data, $replacer);
125
126         // Return replaced code
127         return $code;
128 }
129
130 // Expression call-back function for reading data from $_SERVER
131 function doExpressionServer ($data) {
132         // This will make 'foo_bar' to detectFooBar()
133         $functionName = "'detect' . implode('', array_map('firstCharUpperCase', explode('_', '" . $data['callback'] . "')))";
134
135         // Generate replacer
136         $replacer = '{DQUOTE} . call_user_func(' . $functionName . ') . {DQUOTE}';
137
138         // Replace it
139         $code = replaceExpressionCode($data, $replacer);
140
141         // Return replaced code
142         return $code;
143 }
144
145 // Expression call-back function for getting extension data
146 function doExpressionExt ($data) {
147         // Not installed is default
148         $replacer = 'false';
149
150         // Is the extension installed?
151         if (isExtensionInstalled($data['matches'][4][$data['key']])) {
152                 // Construct call-back function name
153                 $functionName = 'getExtension' . capitalizeUnderscoreString($data['callback']);
154
155                 // Construct call of the function
156                 $replacer = "{DQUOTE} . call_user_func_array('" . $functionName . "', array('" . $data['matches'][4][$data['key']] . "', TRUE)) . {DQUOTE}";
157         } // END - if
158
159         // Generate replacer
160         $replacer = sprintf("&amp;ext=%s&amp;ver=%s&amp;rev={?CURRENT_REPOSITORY_REVISION?}", $data['matches'][4][$data['key']], $replacer);
161
162         // Replace it and insert parameter for GET request
163         $code = replaceExpressionCode($data, $replacer);
164
165         // Debug message
166         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'code=' . $code . ',replacer=' . $replacer . ',key=' . $data['key'] . ',callback=' . $data['callback']);
167
168         // Return replaced code
169         return $code;
170 }
171
172 // Expression call-back function for getting configuration data
173 // @TODO FILTER_COMPILE_CONFIG does not handle call-back functions so we handle it here again
174 function doExpressionConfig ($data) {
175         // Is there a special expression function for it?
176         if (isExpressionFunctionAvaiable($data)) {
177                 // Then use it
178                 $replacer = '{DQUOTE}  . ' . $data['callback'] . '(' . getExpressionFunction($data) . '()) . {DQUOTE}';
179         } elseif (!isConfigEntrySet($data['matches'][4][$data['key']])) {
180                 // Config entry is not set
181                 $replacer = '{DQUOTE}  . ' . $data['callback'] . '(NULL) . {DQUOTE}';
182         } else {
183                 // Default replacer is the config value itself
184                 $replacer = '{DQUOTE}  . ' . $data['callback'] . '(getConfig(' . chr(39) . $data['matches'][4][$data['key']] . chr(39) . ')) . {DQUOTE}';
185         }
186
187         // Replace the config entry
188         $code = replaceExpressionCode($data, $replacer);
189
190         // Return replaced code
191         return $code;
192 }
193
194 // Expression call-back function for piping data through functions
195 function doExpressionPipe ($data) {
196         // We need callback and extra_func: callback is really the call-back function, extra_func is our value
197         $replacer = $data['extra_func'];
198
199         // Is there a call-back? Should always be there!
200         if (!empty($data['callback'])) {
201                 //* DEBUG: */ if ($data['callback'] == 'getMemberId') die('<pre>'.encodeEntities(print_r($data, TRUE)).'</pre>');
202                 // If the value is empty, we don't add it
203                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value[' . gettype($data['value']) . ']=' . $data['value']);
204                 if ((empty($data['value'])) && ($data['value'] != '0')) {
205                         // No value is set
206                         $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '())) . {DQUOTE}';
207                 } elseif (isXmlTypeBool($data['value'])) {
208                         // Boolean value detected
209                         $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '(' . $data['value'] . '))) . {DQUOTE}';
210                 } else {
211                         // Some string/integer value is set
212                         $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . "('" . $data['value'] . "'))) . {DQUOTE}";
213                 }
214         } // END - if
215
216         // Replace the config entry
217         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer);
218         $code = replaceExpressionCode($data, $replacer);
219
220         // Return replaced code
221         return $code;
222 }
223
224 // Expression call-back function for calling filters
225 function doExpressionFilter ($data) {
226         // Construct replacement
227         $replacer = "{DQUOTE} . runFilterChain('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}";
228
229         // Run the filter and insert result
230         $code = replaceExpressionCode($data, $replacer);
231
232         // Return replaced code
233         return $code;
234 }
235
236 // Expression call-back function for validator links
237 function doExpressionValidatorLinks ($data) {
238         // Default is nothing
239         $replacer = '';
240
241         // Get the code from data array for replacement/pipe-through
242         $code = $data['code'];
243
244         // Should we generally include validator links?
245         if ((isExtensionInstalled('validator')) && (getConfig('enable_validator') == 'Y') && (!in_array(getModule(), array('admin', 'login')))) {
246                 // Load the validator template
247                 $replacer = escapeQuotes(loadTemplate('validator_links', TRUE));
248         } // END - if
249
250         // Replace the code
251         $code = replaceExpressionCode($data, $replacer);
252
253         // Return the (maybe) replaced code
254         return $code;
255 }
256
257 // Expression call-back for dynamic messages
258 function doExpressionMessage ($data) {
259         // Debug message
260         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'callback=' . $data['callback'] . ',extra_func=' . $data['extra_func'] . ',value=' . $data['value']);
261
262         // Message string replacement depends on if message is masked
263         if ((isMessageMasked($data['callback'], FALSE)) && ((!empty($data['extra_func'])) || ($data['extra_func'] == '0'))) {
264                 // Message should be masked
265                 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['extra_func'] . "') . {DQUOTE}";
266         } elseif (!empty($data['value'])) {
267                 // value is set, so it is masked message
268                 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['value'] . "') . {DQUOTE}";
269         } else {
270                 // Regular message
271                 $replacer = "{DQUOTE} . getMessage('" . $data['callback'] . "') . {DQUOTE}";
272         }
273
274         // Replace the code
275         $code = replaceExpressionCode($data, $replacer);
276
277         // Return the (maybe) replaced code
278         return $code;
279 }
280
281 // Expression call-back for template functions
282 function doExpressionTemplate ($data) {
283         // Construct call-back function name
284         $callbackFunction = 'doTemplate' . ucfirst($data['callback']);
285
286         // Init replacer
287         $replacer = '<!-- [' . __FUNCTION__ . ':' . __LINE__.'] Call-back function ' . $callbackFunction  . ' does not exist. //-->';
288
289         // Is the function there?
290         if (!isset($GLOBALS['current_template'])) {
291                 // This is very bad and needs fixing
292                 reportBug(__FUNCTION__, __LINE__, 'current_template in GLOBALS not set, callbackFunction=' . $callbackFunction . ',function_exists()=' . intval(function_exists($callbackFunction)));
293         } elseif (!function_exists($callbackFunction)) {
294                 // Log missing function only in debug mode
295                 if (isDebugModeEnabled()) {
296                         // Log it here
297                         logDebugMessage(__FUNCTION__, __LINE__, 'Call-back function ' . $callbackFunction . ' does not exist.');
298                 } // END - if
299         } else {
300                 // Do the replacement
301                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
302                 $replacer = '{DQUOTE} . ' . $callbackFunction . '(' . chr(39) . $GLOBALS['current_template'] . chr(39) . ', TRUE';
303
304                 // Is 'value' set?
305                 if (!empty($data['value'])) {
306                         // Then include it as well
307                         $replacer .= ', ' . chr(39) . $data['value'] . chr(39);
308                 } // END - if
309
310                 // Replacer is ready
311                 $replacer .= ') . {DQUOTE}';
312         }
313
314         // Replace the code
315         $code = replaceExpressionCode($data, $replacer);
316
317         // Return the (maybe) replaced code
318         return $code;
319 }
320
321 // Expression call-back for math functions
322 function doExpressionMath ($data) {
323         // Do the replacement
324         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
325         $replacer = '{DQUOTE} . doCalculate' . $data['callback'] . '(' . $data['value'] . ') . {DQUOTE}';
326
327         // Replace the code
328         $code = replaceExpressionCode($data, $replacer);
329
330         // Load include once
331         loadIncludeOnce('inc/math-functions.php');
332
333         // Return the (maybe) replaced code
334         return $code;
335 }
336
337 // Expression call-back for GET request
338 function doExpressionGet ($data) {
339         // Construct the replacer:
340         // - GET request element
341         $replacer = '{%pipe,getRequestElement';
342
343         // Add more call-back functions?
344         if (!empty($data['callback'])) {
345                 // - Okay, add them
346                 $replacer .= ',' . $data['callback'];
347         } // END - if
348
349         // - Finalize replacer
350         $replacer .= '=' . $data['value'] . '%}';
351
352         // Replace the code
353         $code = replaceExpressionCode($data, $replacer);
354
355         // Return the (maybe) replaced code
356         return $code;
357 }
358
359 // Expression call-back for POST request
360 function doExpressionPost ($data) {
361         // Construct the replacer:
362         // - POST request element
363         $replacer = '{%pipe,postRequestElement';
364
365         // Add more call-back functions?
366         if (!empty($data['callback'])) {
367                 // - Okay, add them
368                 $replacer .= ',' . $data['callback'];
369         } // END - if
370
371         // - Finalize replacer
372         $replacer .= '=' . $data['value'] . '%}';
373
374         // Replace the code
375         $code = replaceExpressionCode($data, $replacer);
376
377         // Return the (maybe) replaced code
378         return $code;
379 }
380
381 // Expression call-back for session data
382 function doExpressionSession ($data) {
383         // Construct the replacer:
384         // - Session element
385         $replacer = '{%pipe,getSession';
386
387         // Add more call-back functions?
388         if (!empty($data['callback'])) {
389                 // - Okay, add them
390                 $replacer .= ',' . $data['callback'];
391         } // END - if
392
393         // - Finalize replacer
394         $replacer .= '=' . $data['value'] . '%}';
395
396         // Debug message
397         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',replacer=' . $replacer);
398
399         // Replace the code
400         $code = replaceExpressionCode($data, $replacer);
401
402         // Return the (maybe) replaced code
403         return $code;
404 }
405
406 /**
407  * Expression call-back for session piplining, this means:
408  *
409  * 1) Read session data
410  * 2) Wrap the raw data into {%pipe,fooFunction=$rawValue%}
411  */
412 function doExpressionSessionPipe ($data) {
413         // Get the session data
414         $rawValue = getSession($data['value']);
415
416         // Construct the replacer
417         $replacer = '{%pipe,' . $data['callback'] . '=' . $rawValue . '%}';
418
419         // Debug message
420         //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',rawValue=' . $rawValue . ',replacer=' . $replacer);
421
422         // Replace the code
423         $code = replaceExpressionCode($data, $replacer);
424
425         // Return the (maybe) replaced code
426         return $code;
427 }
428
429 // Expression call-back for formulars
430 function doExpressionForm ($data) {
431         // Default method is GET, target is _self
432         $data['__form_method'] = 'get';
433         $data['__form_target'] = '_self';
434         $data['__form_name']   = 'form';
435         $data['__form_id']     = 'form';
436         $data['__server']      = '';
437
438         // Check which method/target is set
439         foreach (array('callback', 'extra_func', 'extra_func2') as $key) {
440                 // Make lower-case
441                 $value = strtolower($data[$key]);
442
443                 // Is formMethodPost set?
444                 if ($value == 'formmethodpost') {
445                         // Use it
446                         $data['__form_method'] = 'post';
447                 } elseif (($value == 'formmethodpost') && (!isSpider()) && (!isValidSession())) {
448                         // Then expand 'value' with session id
449                         if (strpos($data['value'], '?') !== FALSE) {
450                                 // '?' is set
451                                 $data['value'] .= '&amp';
452                         } else {
453                                 // Is not set
454                                 $data['value'] .= '?';
455                         }
456
457                         // Append session's name and id
458                         $data['value'] .= session_name() . '=' . session_id();
459                 } elseif (substr($value, 0, 10) == 'formtarget') {
460                         // Form target is found
461                         $data['__form_target'] = substr($value, 10);
462                 } elseif (substr($value, 0, 8) == 'formname') {
463                         // Form name is found
464                         $data['__form_name'] = substr($value, 8);
465                 } elseif (substr($value, 0, 6) == 'formid') {
466                         // Form id found
467                         $data['__form_id'] = substr($value, 6);
468                 } elseif (substr($value, 0, 6) == 'server') {
469                         // {%server,foo%} found
470                         $data['__server'] = '{%server,' . substr($value, 6) . '%}';
471                 }
472         } // END - foreach
473
474         // Generate the replacement code which is the opening form tag
475         $data['__replacer'] = '<form accept-charset=\"UTF-8\"';
476         if (!empty($data['value'])) {
477                 $data['__replacer'] .= ' action=\"{%url=' . $data['value'];
478                 if (!empty($data['__server'])) {
479                         $data['__replacer'] .= $data['__server'];
480                 } // END - if
481                 $data['__replacer'] .= '%}\"';
482         } // END - if
483
484         // Add rest elements
485         foreach (array('method', 'target', 'name', 'id') as $key) {
486                 $data['__replacer'] .= ' ' . $key . '=\"' . $data['__form_' . $key] . '\"';
487         } // END - foreach
488
489         // Close the tag here (don't move it below the next filter)
490         $data['__replacer'] .= '>' . PHP_EOL;
491
492         /*
493          * Call a filter chain to allow more hidden fields being added. You should
494          * not remove the > char from above line to add onsubmit="" or so. Instead
495          * you should better use jquery to accomplish the same.
496          */
497         $data = runFilterChain('open_form_fields', $data);
498
499         // Replace the code
500         $code = replaceExpressionCode($data, $data['__replacer']);
501
502         // Return the (maybe) replaced code
503         return $code;
504 }
505
506 // Expression call-back to close form tags
507 function doExpressionFormClose ($data) {
508         // Initial replacer is really easy ...
509         $data['__replacer'] = '</form>' . PHP_EOL;
510
511         /*
512          * Call a filter chain to allow more hidden fields being added at the end
513          * of the form.
514          */
515         $data = runFilterChain('close_form_fields', $data);
516
517         // Replace the code
518         $code = replaceExpressionCode($data, $data['__replacer']);
519
520         // Return the (maybe) replaced code
521         return $code;
522 }
523
524 // Expression call-back to handle jquery inclusion
525 function doExpressionJquery ($data) {
526         // Default is compressed
527         $jquery = 'jquery';
528         $data['output_mode'] = '';
529
530         // Is there JS-mode?
531         if ($data['callback'] == 'js') {
532                 // Switch to it
533                 $data['output_mode'] = ',js';
534         } // END - if
535
536         // Is there a value?
537         if ($data['callback'] == 'js') {
538                 // Then load special library
539                 $jquery .= '-' . $data['value'];
540         } // END - if
541
542         // Is debug mode enabled?
543         if ((isGetRequestElementSet('jquery')) || (isSessionVariableSet('jquery'))) {
544                 // Then use uncompressed
545                 $jquery .= '.uncompressed';
546
547                 // Remember it in session
548                 setSession('jquery', '1');
549         } // END - if
550
551         // Add {%url%} around it
552         $replacer = '{%url' . $data['output_mode'] . '=js/' . $jquery . '.js?dummy=1%}';
553
554         // Replace the code
555         $code = replaceExpressionCode($data, $replacer);
556
557         // Return the (maybe) replaced code
558         return $code;
559 }
560
561 // [EOF]
562 ?>