includes/template.php
changeset 162 e1a22031b5bd
parent 142 ca9118d9c0f2
child 163 ad00dc1f8706
equal deleted inserted replaced
161:bed9d04fa144 162:e1a22031b5bd
   869       return $t;
   869       return $t;
   870     }
   870     }
   871     else return '';
   871     else return '';
   872   }
   872   }
   873   
   873   
   874   function process_template($file) {
   874   /**
       
   875    * Compiles and executes a template based on the current variables and booleans. Loads
       
   876    * the theme and initializes variables if needed. This mostly just calls child functions.
       
   877    * @param string File to process
       
   878    * @return string
       
   879    */
       
   880   
       
   881   function process_template($file)
       
   882   {
   875     global $db, $session, $paths, $template, $plugins; // Common objects
   883     global $db, $session, $paths, $template, $plugins; // Common objects
   876     if(!defined('ENANO_TEMPLATE_LOADED'))
   884     if(!defined('ENANO_TEMPLATE_LOADED'))
   877     {
   885     {
   878       $this->load_theme();
   886       $this->load_theme();
   879       $this->init_vars();
   887       $this->init_vars();
   880     }
   888     }
   881     eval($this->compile_template($file));
   889     
   882     return $tpl_code;
   890     $compiled = $this->compile_template($file);
   883   }
   891     return eval($compiled);
   884   
   892   }
   885   function extract_vars($file) {
   893   
   886     global $db, $session, $paths, $template, $plugins; // Common objects
   894   /**
   887     if(!$this->theme)
   895    * Loads variables from the specified template file. Returns an associative array containing the variables.
       
   896    * @param string Template file to process (elements.tpl)
       
   897    * @return array
       
   898    */
       
   899   
       
   900   function extract_vars($file)
       
   901   {
       
   902     global $db, $session, $paths, $template, $plugins; // Common objects
       
   903     
       
   904     // Sometimes this function gets called before the theme is loaded
       
   905     // This is a bad coding practice so this function will always be picky.
       
   906     if ( !$this->theme )
   888     {
   907     {
   889       die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.<br /><br />Backtrace, most recent call first:<pre>'.enano_debug_print_backtrace(true).'</pre>');
   908       die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.<br /><br />Backtrace, most recent call first:<pre>'.enano_debug_print_backtrace(true).'</pre>');
   890     }
   909     }
   891     if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting');
   910     
   892     $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file);
   911     // Full pathname of template file
       
   912     $tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $file;
       
   913     
       
   914     // Make sure the template even exists
       
   915     if ( !is_file($tpl_file_fullpath) )
       
   916     {
       
   917       die_semicritical('Cannot find template file',
       
   918                        '<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for
       
   919                            the current theme.</p>
       
   920                         <p>Additional debugging information:<br />
       
   921                            <b>Theme currently in use: </b>' . $this->theme . '<br />
       
   922                            <b>Requested file: </b>' . $file . '
       
   923                            </p>');
       
   924     }
       
   925     // Retrieve file contents
       
   926     $text = file_get_contents($tpl_file_fullpath);
       
   927     if ( !$text )
       
   928     {
       
   929       return false;
       
   930     }
       
   931     
       
   932     // Get variables, regular expressions FTW
   893     preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
   933     preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
       
   934     
       
   935     // Initialize return values
   894     $tplvars = Array();
   936     $tplvars = Array();
   895     for($i=0;$i<sizeof($matches[1]);$i++)
   937     
   896     {
   938     // Loop through each match, setting $tplvars[ $first_subpattern ] to $second_subpattern
   897       $tplvars[$matches[1][$i]] = $matches[2][$i];
   939     for ( $i = 0; $i < sizeof($matches[1]); $i++ )
   898     }
   940     {
       
   941       $tplvars[ $matches[1][$i] ] = $matches[2][$i];
       
   942     }
       
   943     
       
   944     // All done!
   899     return $tplvars;
   945     return $tplvars;
   900   }
   946   }
   901   function compile_template($text) {
   947   
   902     global $db, $session, $paths, $template, $plugins; // Common objects
   948   /**
   903     if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting');
   949    * Compiles a block of template code.
   904     $n = $text;
   950    * @param string The text to process
   905     $tpl_filename = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $n) . '.php';
   951    * @return string
   906     if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting');
   952    */
   907     if(file_exists($tpl_filename) && getConfig('cache_thumbs')=='1')
   953   
   908     {
   954   function compile_tpl_code($text)
   909       include($tpl_filename);
   955   {
   910       $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text);
   956     // A random seed used to salt tags
   911       if(isset($md5) && $md5 == md5($text)) {
   957     $seed = md5 ( microtime() . mt_rand() );
       
   958     
       
   959     // Strip out PHP sections
       
   960     preg_match_all('/<\?php(.+?)\?>/is', $text, $php_matches);
       
   961     
       
   962     foreach ( $php_matches[0] as $i => $match )
       
   963     {
       
   964       // Substitute the PHP section with a random tag
       
   965       $tag = "{PHP:$i:$seed}";
       
   966       $text = str_replace_once($match, $tag, $text);
       
   967     }
       
   968     
       
   969     // Escape slashes and single quotes in template code
       
   970     $text = str_replace('\\', '\\\\', $text);
       
   971     $text = str_replace('\'', '\\\'', $text);
       
   972     
       
   973     // Initialize the PHP compiled code
       
   974     $text = 'ob_start(); echo \''.$text.'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;';
       
   975     
       
   976     ##
       
   977     ## Main rules
       
   978     ##
       
   979     
       
   980     //
       
   981     // Conditionals
       
   982     //
       
   983     
       
   984     // If-else-end
       
   985     $text = preg_replace('/<!-- BEGIN ([A-z0-9_-]+?) -->(.*?)<!-- BEGINELSE \\1 -->(.*?)<!-- END \\1 -->/is', '\'; if ( $this->tpl_bool[\'\\1\'] ) { echo \'\\2\'; } else { echo \'\\3\'; } echo \'', $text);
       
   986     
       
   987     // If-end
       
   988     $text = preg_replace('/<!-- BEGIN ([A-z0-9_-]+?) -->(.*?)<!-- END \\1 -->/is', '\'; if ( $this->tpl_bool[\'\\1\'] ) { echo \'\\2\'; } echo \'', $text);
       
   989     
       
   990     // If not-else-end
       
   991     $text = preg_replace('/<!-- BEGINNOT ([A-z0-9_-]+?) -->(.*?)<!-- BEGINELSE \\1 -->(.*?)<!-- END \\1 -->/is', '\'; if ( !$this->tpl_bool[\'\\1\'] ) { echo \'\\2\'; } else { echo \'\\3\'; } echo \'', $text);
       
   992     
       
   993     // If not-end
       
   994     $text = preg_replace('/<!-- BEGINNOT ([A-z0-9_-]+?) -->(.*?)<!-- END \\1 -->/is', '\'; if ( !$this->tpl_bool[\'\\1\'] ) { echo \'\\2\'; } echo \'', $text);
       
   995     
       
   996     // If set-else-end
       
   997     $text = preg_replace('/<!-- IFSET ([A-z0-9_-]+?) -->(.*?)<!-- BEGINELSE \\1 -->(.*?)<!-- END \\1 -->/is', '\'; if ( isset($this->tpl_strings[\'\\1\']) ) { echo \'\\2\'; } else { echo \'\\3\'; } echo \'', $text);
       
   998     
       
   999     // If set-end
       
  1000     $text = preg_replace('/<!-- IFSET ([A-z0-9_-]+?) -->(.*?)<!-- END \\1 -->/is', '\'; if ( isset($this->tpl_strings[\'\\1\']) ) { echo \'\\2\'; } echo \'', $text);
       
  1001     
       
  1002     // If plugin loaded-else-end
       
  1003     $text = preg_replace('/<!-- IFPLUGIN ([A-z0-9_\.-]+?) -->(.*?)<!-- BEGINELSE \\1 -->(.*?)<!-- END \\1 -->/is', '\'; if ( getConfig(\'plugin_\\1\') == \'1\' ) { echo \'\\2\'; } else { echo \'\\3\'; } echo \'', $text);
       
  1004     
       
  1005     // If plugin loaded-end
       
  1006     $text = preg_replace('/<!-- IFPLUGIN ([A-z0-9_\.-]+?) -->(.*?)<!-- END \\1 -->/is', '\'; if ( getConfig(\'plugin_\\1\') == \'1\' ) { echo \'\\2\'; } echo \'', $text);
       
  1007     
       
  1008     //
       
  1009     // Data substitution/variables
       
  1010     //
       
  1011     
       
  1012     // System messages
       
  1013     $text = preg_replace('/<!-- SYSMSG ([A-z0-9\._-]+?) -->/is', '\' . $this->tplWikiFormat($pages->sysMsg(\'\\1\')) . \'', $text);
       
  1014     
       
  1015     // Template variables
       
  1016     $text = preg_replace('/\{([A-z0-9_-]+?)\}/is', '\' . $this->tpl_strings[\'\\1\'] . \'', $text);
       
  1017     
       
  1018     // Reinsert PHP
       
  1019     
       
  1020     foreach ( $php_matches[1] as $i => $match )
       
  1021     {
       
  1022       // Substitute the random tag with the "real" PHP code
       
  1023       $tag = "{PHP:$i:$seed}";
       
  1024       $text = str_replace_once($tag, "'; $match echo '", $text);
       
  1025     }
       
  1026     
       
  1027     return $text;  
       
  1028     
       
  1029   }
       
  1030   
       
  1031   /**
       
  1032    * Compiles the contents of a given template file, possibly using a cached copy, and returns the compiled code.
       
  1033    * @param string Filename of template (header.tpl)
       
  1034    * @return string
       
  1035    */
       
  1036   
       
  1037   function compile_template($filename)
       
  1038   {
       
  1039     global $db, $session, $paths, $template, $plugins; // Common objects
       
  1040     
       
  1041     // Full path to template file
       
  1042     $tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $filename;
       
  1043     
       
  1044     // Make sure the file exists
       
  1045     if ( !is_file($tpl_file_fullpath) )
       
  1046     {
       
  1047       die_semicritical('Cannot find template file',
       
  1048                        '<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for
       
  1049                            the current theme.</p>
       
  1050                         <p>Additional debugging information:<br />
       
  1051                            <b>Theme currently in use: </b>' . $this->theme . '<br />
       
  1052                            <b>Requested file: </b>' . $file . '
       
  1053                            </p>');
       
  1054     }
       
  1055     
       
  1056     // Check for cached copy
       
  1057     // This will make filenames in the pattern of theme-file.tpl.php
       
  1058     $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
       
  1059     
       
  1060     // Only use cached copy if caching is enabled
       
  1061     //   (it is enabled by default I think)
       
  1062     if ( file_exists($cache_file) && getConfig('cache_thumbs') == '1' )
       
  1063     {
       
  1064       // Cache files are auto-generated, but otherwise are normal PHP files
       
  1065       include($cache_file);
       
  1066       
       
  1067       // Fetch content of the ORIGINAL
       
  1068       $text = file_get_contents($tpl_file_fullpath);
       
  1069       
       
  1070       // $md5 will be set by the cached file
       
  1071       // This makes sure that a cached copy of the template is used only if its MD5
       
  1072       // matches the MD5 of the file that the compiled file was compiled from.
       
  1073       if ( isset($md5) && $md5 == md5($text) )
       
  1074       {
   912         return str_replace('\\"', '"', $tpl_text);
  1075         return str_replace('\\"', '"', $tpl_text);
   913       }
  1076       }
   914     }
  1077     }
   915     $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$n);
  1078     
   916     
  1079     // We won't use the cached copy here
       
  1080     $text = file_get_contents($tpl_file_fullpath);
       
  1081     
       
  1082     // This will be used later when writing the cached file
   917     $md5 = md5($text);
  1083     $md5 = md5($text);
   918     
  1084     
   919     $seed = md5 ( microtime() . mt_rand() );
  1085     // Preprocessing and checks complete - compile the code
   920     preg_match_all("/<\?php(.*?)\?>/is", $text, $m);
  1086     $text = $this->compile_tpl_code($text);
   921     //die('<pre>'.htmlspecialchars(print_r($m, true)).'</pre>');
  1087     
   922     for($i = 0; $i < sizeof($m[1]); $i++)
  1088     // Perhaps caching is enabled and the admin has changed the template?
   923     {
  1089     if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' )
   924       $text = str_replace("<?php{$m[1][$i]}?>", "{PHPCODE:{$i}:{$seed}}", $text);
  1090     {
   925     }
  1091       $h = fopen($cache_file, 'w');
   926     //die('<pre>'.htmlspecialchars($text).'</pre>');
  1092       if ( !$h )
   927     $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean();';
  1093       {
   928     $text = preg_replace('#<!-- BEGIN (.*?) -->#is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text);
  1094         // Couldn't open the file - silently ignore and return
   929     $text = preg_replace('#<!-- IFSET (.*?) -->#is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text);
  1095         return $text;
   930     $text = preg_replace('#<!-- IFPLUGIN (.*?) -->#is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text);
  1096       }
   931     $text = preg_replace('#<!-- SYSMSG (.*?) -->#is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text);
  1097       
   932     $text = preg_replace('#<!-- BEGINNOT (.*?) -->#is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text);
  1098       // Escape the compiled code so it can be eval'ed
   933     $text = preg_replace('#<!-- BEGINELSE (.*?) -->#is', '\'; } else { echo \'', $text);
  1099       $text_escaped = addslashes($text);
   934     $text = preg_replace('#<!-- END (.*?) -->#is', '\'; } echo \'', $text);
       
   935     $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text);
       
   936     for($i = 0; $i < sizeof($m[1]); $i++)
       
   937     {
       
   938       $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text);
       
   939     }
       
   940     if(is_writable(ENANO_ROOT.'/cache/') && getConfig('cache_thumbs')=='1')
       
   941     {
       
   942       //die($tpl_filename);
       
   943       $h = fopen($tpl_filename, 'w');
       
   944       if(!$h) return $text;
       
   945       $t = addslashes($text);
       
   946       $notice = <<<EOF
  1100       $notice = <<<EOF
   947 
  1101 
   948 /*
  1102 /*
   949  * NOTE: This file was automatically generated by Enano and is based on compiled code. Do not edit this file.
  1103  * NOTE: This file was automatically generated by Enano and is based on compiled code. Do not edit this file.
   950  * If you edit this file, any changes you make will be lost the next time the associated source template file is edited.
  1104  * If you edit this file, any changes you make will be lost the next time the associated source template file is edited.
   951  */
  1105  */
   952 
  1106 
   953 EOF;
  1107 EOF;
   954       fwrite($h, '<?php ' . $notice . ' $md5 = \''.$md5.'\'; $tpl_text = \''.$t.'\'; ?>');
  1108       // This is really just a normal PHP file that sets a variable or two and exits.
       
  1109       // $tpl_text actually will contain the compiled code
       
  1110       fwrite($h, '<?php ' . $notice . ' $md5 = \'' . $md5 . '\'; $tpl_text = \'' . $text_escaped . '\'; ?>');
   955       fclose($h);
  1111       fclose($h);
   956     }
  1112     }
       
  1113     
   957     return $text; //('<pre>'.htmlspecialchars($text).'</pre>');
  1114     return $text; //('<pre>'.htmlspecialchars($text).'</pre>');
   958   }
  1115   }
   959   
  1116   
   960   function compile_template_text($text) {
  1117   
   961     $seed = md5 ( microtime() . mt_rand() );
  1118   /**
   962     preg_match_all("/<\?php(.*?)\?>/is", $text, $m);
  1119    * Compiles (parses) some template code with the current master set of variables and booleans.
   963     //die('<pre>'.htmlspecialchars(print_r($m, true)).'</pre>');
  1120    * @param string Text to process
   964     for($i = 0; $i < sizeof($m[1]); $i++)
  1121    * @return string
   965     {
  1122    */
   966       $text = str_replace("<?php{$m[1][$i]}?>", "{PHPCODE:{$i}:{$seed}}", $text);
  1123   
   967     }
  1124   function compile_template_text($text)
   968     //die('<pre>'.htmlspecialchars($text).'</pre>');
  1125   {
   969     $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;';
  1126     // this might do something else in the future, possibly cache large templates
   970     $text = preg_replace('#<!-- BEGIN (.*?) -->#is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text);
  1127     return $this->compile_tpl_code($text);
   971     $text = preg_replace('#<!-- IFSET (.*?) -->#is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text);
  1128   }
   972     $text = preg_replace('#<!-- IFPLUGIN (.*?) -->#is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text);
  1129   
   973     $text = preg_replace('#<!-- SYSMSG (.*?) -->#is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text);
  1130   /**
   974     $text = preg_replace('#<!-- BEGINNOT (.*?) -->#is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text);
  1131    * For convenience - compiles AND parses some template code.
   975     $text = preg_replace('#<!-- BEGINELSE (.*?) -->#is', '\'; } else { echo \'', $text);
  1132    * @param string Text to process
   976     $text = preg_replace('#<!-- END (.*?) -->#is', '\'; } echo \'', $text);
  1133    * @return string
   977     $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text);
  1134    */
   978     for($i = 0; $i < sizeof($m[1]); $i++)
       
   979     {
       
   980       $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text);
       
   981     }
       
   982     return $text; //('<pre>'.htmlspecialchars($text).'</pre>');
       
   983   }
       
   984   
  1135   
   985   function parse($text)
  1136   function parse($text)
   986   {
  1137   {
   987     $text = $this->compile_template_text($text);
  1138     $text = $this->compile_template_text($text);
   988     return eval($text);
  1139     return eval($text);
  1002   
  1153   
  1003   // The template language is really a miniature programming language; with variables, conditionals, everything!
  1154   // The template language is really a miniature programming language; with variables, conditionals, everything!
  1004   // So you can implement custom logic into your sidebar if you wish.
  1155   // So you can implement custom logic into your sidebar if you wish.
  1005   // "Real" PHP support coming soon :-D
  1156   // "Real" PHP support coming soon :-D
  1006   
  1157   
  1007   function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl') {
  1158   /**
       
  1159    * Takes a blob of HTML with the specially formatted template-oriented wikitext and formats it. Does not use eval().
       
  1160    * This function butchers every coding standard in Enano and should eventually be rewritten. The fact is that the
       
  1161    * code _works_ and does a good job of checking for errors and cleanly complaining about them.
       
  1162    * @param string Text to process
       
  1163    * @param bool Ignored for backwards compatibility
       
  1164    * @param string File to get variables for sidebar data from
       
  1165    * @return string
       
  1166    */
       
  1167   
       
  1168   function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl')
       
  1169   {
  1008     global $db, $session, $paths, $template, $plugins; // Common objects
  1170     global $db, $session, $paths, $template, $plugins; // Common objects
  1009     $filter_links = false;
  1171     $filter_links = false;
  1010     $tplvars = $this->extract_vars($filename);
  1172     $tplvars = $this->extract_vars($filename);
  1011     if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
  1173     if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
  1012     else $as = '';
  1174     else $as = '';
  1027       $message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message);
  1189       $message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message);
  1028     }
  1190     }
  1029     
  1191     
  1030     // Conditionals
  1192     // Conditionals
  1031     
  1193     
  1032     preg_match_all('#\{if ([A-Za-z0-9_ &\|\!-]*)\}(.*?)\{\/if\}#is', $message, $links);
  1194     preg_match_all('#\{if ([A-Za-z0-9_ \(\)&\|\!-]*)\}(.*?)\{\/if\}#is', $message, $links);
  1033     
  1195     
  1034     for($i=0;$i<sizeof($links[1]);$i++)
  1196     // Temporary exception from coding standards - using tab length of 4 here for clarity
  1035     {
  1197     for ( $i = 0; $i < sizeof($links[1]); $i++ )
  1036       $message = str_replace('{if '.$links[1][$i].'}'.$links[2][$i].'{/if}', '{CONDITIONAL:'.$i.':'.$random_id.'}', $message);
  1198     {
  1037       
  1199         $condition =& $links[1][$i];
  1038       // Time for some manual parsing...
  1200         $message = str_replace('{if '.$condition.'}'.$links[2][$i].'{/if}', '{CONDITIONAL:'.$i.':'.$random_id.'}', $message);
  1039       $chk = false;
       
  1040       $current_id = '';
       
  1041       $prn_level = 0;
       
  1042       // Used to keep track of where we are in the conditional
       
  1043       // Object of the game: turn {if this && ( that OR !something_else )} ... {/if} into if( ( isset($this->tpl_bool['that']) && $this->tpl_bool['that'] ) && ...
       
  1044       // Method of attack: escape all variables, ignore all else. Non-valid code is filtered out by a regex above.
       
  1045       $in_var_now = true;
       
  1046       $in_var_last = false;
       
  1047       $current_var = '';
       
  1048       $current_var_start_pos = 0;
       
  1049       $current_var_end_pos   = 0;
       
  1050       $j = -1;
       
  1051       $links[1][$i] = $links[1][$i] . ' ';
       
  1052       $d = strlen($links[1][$i]);
       
  1053       while($j < $d)
       
  1054       {
       
  1055         $j++;
       
  1056         $in_var_last = $in_var_now;
       
  1057         
  1201         
  1058         $char = substr($links[1][$i], $j, 1);
  1202         // Time for some manual parsing...
  1059         $in_var_now = ( preg_match('#^([A-z0-9_]*){1}$#', $char) ) ? true : false;
  1203         $chk = false;
  1060         if(!$in_var_last && $in_var_now)
  1204         $current_id = '';
       
  1205         $prn_level = 0;
       
  1206         // Used to keep track of where we are in the conditional
       
  1207         // Object of the game: turn {if this && ( that OR !something_else )} ... {/if} into if( ( isset($this->tpl_bool['that']) && $this->tpl_bool['that'] ) && ...
       
  1208         // Method of attack: escape all variables, ignore all else. Non-valid code is filtered out by a regex above.
       
  1209         $in_var_now = true;
       
  1210         $in_var_last = false;
       
  1211         $current_var = '';
       
  1212         $current_var_start_pos = 0;
       
  1213         $current_var_end_pos     = 0;
       
  1214         $j = -1;
       
  1215         $condition = $condition . ' ';
       
  1216         $d = strlen($condition);
       
  1217         while($j < $d)
  1061         {
  1218         {
  1062           $current_var_start_pos = $j;
  1219             $j++;
       
  1220             $in_var_last = $in_var_now;
       
  1221             
       
  1222             $char = substr($condition, $j, 1);
       
  1223             $in_var_now = ( preg_match('#^([A-z0-9_]*){1}$#', $char) ) ? true : false;
       
  1224             if(!$in_var_last && $in_var_now)
       
  1225             {
       
  1226                 $current_var_start_pos = $j;
       
  1227             }
       
  1228             if($in_var_last && !$in_var_now)
       
  1229             {
       
  1230                 $current_var_end_pos = $j;
       
  1231             }
       
  1232             if($in_var_now)
       
  1233             {
       
  1234                 $current_var .= $char;
       
  1235                 continue;
       
  1236             }
       
  1237             // OK we are not inside of a variable. That means that we JUST hit the end because the counter ($j) will be advanced to the beginning of the next variable once processing here is complete.
       
  1238             if($char != ' ' && $char != '(' && $char != ')' && $char != 'A' && $char != 'N' && $char != 'D' && $char != 'O' && $char != 'R' && $char != '&' && $char != '|' && $char != '!' && $char != '<' && $char != '>' && $char != '0' && $char != '1' && $char != '2' && $char != '3' && $char != '4' && $char != '5' && $char != '6' && $char != '7' && $char != '8' && $char != '9')
       
  1239             {
       
  1240                 // XSS attack! Bail out
       
  1241                 $errmsg    = '<p><b>Error:</b> Syntax error (possibly XSS attack) caught in template code:</p>';
       
  1242                 $errmsg .= '<pre>';
       
  1243                 $errmsg .= '{if '.htmlspecialchars($condition).'}';
       
  1244                 $errmsg .= "\n    ";
       
  1245                 for ( $k = 0; $k < $j; $k++ )
       
  1246                 {
       
  1247                     $errmsg .= " ";
       
  1248                 }
       
  1249                 // Show position of error
       
  1250                 $errmsg .= '<span style="color: red;">^</span>';
       
  1251                 $errmsg .= '</pre>';
       
  1252                 $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $errmsg, $message);
       
  1253                 continue 2;
       
  1254             }
       
  1255             if($current_var != '')
       
  1256             {
       
  1257                 $cd = '( isset($this->tpl_bool[\''.$current_var.'\']) && $this->tpl_bool[\''.$current_var.'\'] )';
       
  1258                 $cvt = substr($condition, 0, $current_var_start_pos) . $cd . substr($condition, $current_var_end_pos, strlen($condition));
       
  1259                 $j = $j + strlen($cd) - strlen($current_var);
       
  1260                 $current_var = '';
       
  1261                 $condition = $cvt;
       
  1262                 $d = strlen($condition);
       
  1263             }
  1063         }
  1264         }
  1064         if($in_var_last && !$in_var_now)
  1265         $condition = substr($condition, 0, strlen($condition)-1);
       
  1266         $condition = '$chk = ( '.$condition.' ) ? true : false;';
       
  1267         eval($condition);
       
  1268         
       
  1269         if($chk)
  1065         {
  1270         {
  1066           $current_var_end_pos = $j;
  1271             if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}'));
       
  1272             else $c = $links[2][$i];
       
  1273             $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
  1067         }
  1274         }
  1068         if($in_var_now)
  1275         else
  1069         {
  1276         {
  1070           $current_var .= $char;
  1277             if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i]));
  1071           continue;
  1278             else $c = '';
       
  1279             $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
  1072         }
  1280         }
  1073         // OK we are not inside of a variable. That means that we JUST hit the end because the counter ($j) will be advanced to the beginning of the next variable once processing here is complete.
       
  1074         if($char != ' ' && $char != '(' && $char != ')' && $char != 'A' && $char != 'N' && $char != 'D' && $char != 'O' && $char != 'R' && $char != '&' && $char != '|' && $char != '!' && $char != '<' && $char != '>' && $char != '0' && $char != '1' && $char != '2' && $char != '3' && $char != '4' && $char != '5' && $char != '6' && $char != '7' && $char != '8' && $char != '9')
       
  1075         {
       
  1076           // XSS attack! Bail out
       
  1077           echo '<p><b>Error:</b> Syntax error (possibly XSS attack) caught in template code:</p>';
       
  1078           echo '<pre>';
       
  1079           echo '{if '.$links[1][$i].'}';
       
  1080           echo "\n    ";
       
  1081           for($k=0;$k<$j;$k++) echo " ";
       
  1082           echo '<span style="color: red;">^</span>';
       
  1083           echo '</pre>';
       
  1084           continue 2;
       
  1085         }
       
  1086         if($current_var != '')
       
  1087         {
       
  1088           $cd = '( isset($this->tpl_bool[\''.$current_var.'\']) && $this->tpl_bool[\''.$current_var.'\'] )';
       
  1089           $cvt = substr($links[1][$i], 0, $current_var_start_pos) . $cd . substr($links[1][$i], $current_var_end_pos, strlen($links[1][$i]));
       
  1090           $j = $j + strlen($cd) - strlen($current_var);
       
  1091           $current_var = '';
       
  1092           $links[1][$i] = $cvt;
       
  1093           $d = strlen($links[1][$i]);
       
  1094         }
       
  1095       }
       
  1096       $links[1][$i] = substr($links[1][$i], 0, strlen($links[1][$i])-1);
       
  1097       $links[1][$i] = '$chk = ( '.$links[1][$i].' ) ? true : false;';
       
  1098       eval($links[1][$i]);
       
  1099       
       
  1100       if($chk) { // isset($this->tpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]]
       
  1101         if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}'));
       
  1102         else $c = $links[2][$i];
       
  1103         $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
       
  1104       } else {
       
  1105         if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i]));
       
  1106         else $c = '';
       
  1107         $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
       
  1108       }
       
  1109     }
  1281     }
  1110     
  1282     
  1111     preg_match_all('#\{!if ([A-Za-z_-]*)\}(.*?)\{\/if\}#is', $message, $links);
  1283     preg_match_all('#\{!if ([A-Za-z_-]*)\}(.*?)\{\/if\}#is', $message, $links);
  1112     
  1284     
  1113     for($i=0;$i<sizeof($links[1]);$i++)
  1285     for($i=0;$i<sizeof($links[1]);$i++)