Another sweep from the optimization monster.
template: rewrote parts of tplWikiFormat to do faster validation and less eval()ing
template: sidebar is now cached for anonymous users (thanks to Richard Greene for the suggestion)
dbal: rewrote SQL query parser, much cruft taken out
template: made tplWikiFormat depend more on RenderMan
common: moved some lesser-used includes out of main loading sequence
Expect a lot of changes to AES code in the next commit!
--- a/ajax.php Mon Jun 30 17:22:29 2008 -0400
+++ b/ajax.php Wed Jul 02 19:36:44 2008 -0400
@@ -14,76 +14,6 @@
define('ENANO_INTERFACE_AJAX', '');
- // fillusername should be done without the help of the rest of Enano - all we need is the DBAL
- if ( isset($_GET['_mode']) && $_GET['_mode'] == 'fillusername' )
- {
- // setup and load a very basic, specialized instance of the Enano API
- function microtime_float()
- {
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
- }
- // Determine directory (special case for development servers)
- if ( strpos(__FILE__, '/repo/') && file_exists('.enanodev') )
- {
- $filename = str_replace('/repo/', '/', __FILE__);
- }
- else
- {
- $filename = __FILE__;
- }
- define('ENANO_ROOT', dirname($filename));
- require(ENANO_ROOT.'/includes/functions.php');
- require(ENANO_ROOT.'/includes/dbal.php');
- require(ENANO_ROOT.'/includes/json2.php');
-
- require(ENANO_ROOT . '/config.php');
- unset($dbuser, $dbpasswd);
- if ( !isset($dbdriver) )
- $dbdriver = 'mysql';
-
- $db = new $dbdriver();
-
- $db->connect();
-
- // result is sent using JSON
- $return = Array(
- 'mode' => 'success',
- 'users_real' => Array()
- );
-
- // should be connected to the DB now
- $name = (isset($_GET['name'])) ? $db->escape($_GET['name']) : false;
- if ( !$name )
- {
- $return = array(
- 'mode' => 'error',
- 'error' => 'Invalid URI'
- );
- die( enano_json_encode($return) );
- }
- $allowanon = ( isset($_GET['allowanon']) && $_GET['allowanon'] == '1' ) ? '' : ' AND user_id > 1';
- $q = $db->sql_query('SELECT username FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username) LIKE ' . ENANO_SQLFUNC_LOWERCASE . '(\'%'.$name.'%\')' . $allowanon . ' ORDER BY username ASC;');
- if ( !$q )
- {
- $db->die_json();
- }
- $i = 0;
- while($r = $db->fetchrow())
- {
- $return['users_real'][] = $r['username'];
- $i++;
- }
- $db->free_result();
-
- // all done! :-)
- $db->close();
-
- echo enano_json_encode( $return );
-
- exit;
- }
-
require('includes/common.php');
global $db, $session, $paths, $template, $plugins; // Common objects
@@ -93,6 +23,7 @@
switch($_GET['_mode']) {
case "checkusername":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::checkusername($_GET['name']);
break;
case "getsource":
@@ -230,18 +161,7 @@
break;
case "savepage":
/* **** OBSOLETE **** */
- $summ = ( isset($_POST['summary']) ) ? $_POST['summary'] : '';
- $minor = isset($_POST['minor']);
- $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['text'], $summ, $minor);
- if ( $e == 'good' )
- {
- $page = new PageProcessor($paths->page_id, $paths->namespace);
- $page->send();
- }
- else
- {
- echo '<p>Error saving the page: '.$e.'</p>';
- }
+
break;
case "savepage_json":
header('Content-type: application/json');
@@ -434,6 +354,7 @@
echo enano_json_encode($result);
break;
case "histlist":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::histlist($paths->page_id, $paths->namespace);
break;
case "rollback":
@@ -445,6 +366,7 @@
echo enano_json_encode($result);
break;
case "comments":
+ require_once(ENANO_ROOT.'/includes/comment.php');
$comments = new Comments($paths->page_id, $paths->namespace);
if ( isset($_POST['data']) )
{
@@ -463,33 +385,42 @@
echo enano_json_encode($result);
break;
case "flushlogs":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::flushlogs($paths->page_id, $paths->namespace);
break;
case "deletepage":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
if ( empty($reason) )
die($lang->get('page_err_need_reason'));
echo PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
break;
case "delvote":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::delvote($paths->page_id, $paths->namespace);
break;
case "resetdelvotes":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
break;
case "getstyles":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::getstyles($_GET['id']);
break;
case "catedit":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::catedit($paths->page_id, $paths->namespace);
break;
case "catsave":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
break;
case "setwikimode":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::setwikimode($paths->page_id, $paths->namespace, (int)$_GET['mode']);
break;
case "setpass":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::setpass($paths->page_id, $paths->namespace, $_POST['password']);
break;
case "fillusername":
@@ -537,9 +468,11 @@
}
break;
case "preview":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
echo PageUtils::genPreview($_POST['text']);
break;
case "pagediff":
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
$id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
if(!$id1 || !$id2) { echo '<p>Invalid request.</p>'; $template->footer(); break; }
@@ -558,6 +491,7 @@
else echo $rdns;
break;
case 'acljson':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$parms = ( isset($_POST['acl_params']) ) ? rawurldecode($_POST['acl_params']) : false;
echo PageUtils::acl_json($parms);
break;
--- a/includes/clientside/static/ajax.js Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/clientside/static/ajax.js Wed Jul 02 19:36:44 2008 -0400
@@ -149,7 +149,11 @@
if ( !box )
return false;
- var newname = ( obj.getElementsByTagName('input')[0] ).value;
+ var input = obj.getElementsByTagName('input')[0];
+ console.debug(obj, input);
+ if ( !input )
+ return false;
+ var newname = input.value;
newname = trim(newname);
if ( newname.length < 1 )
--- a/includes/clientside/static/enano-lib-basic.js Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/clientside/static/enano-lib-basic.js Wed Jul 02 19:36:44 2008 -0400
@@ -465,6 +465,7 @@
ajaxEditTheme: 'theme-manager.js',
ajaxToggleSystemThemes: 'theme-manager.js',
ajaxInstallTheme: 'theme-manager.js',
+ ajaxInitRankEdit: 'rank-manager.js'
};
var placeholder_instances = {};
--- a/includes/clientside/static/functions.js Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/clientside/static/functions.js Wed Jul 02 19:36:44 2008 -0400
@@ -558,6 +558,7 @@
return z;
}
+var shift = false;
function isKeyPressed(event)
{
if (event.shiftKey==1)
--- a/includes/common.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/common.php Wed Jul 02 19:36:44 2008 -0400
@@ -138,20 +138,10 @@
require_once(ENANO_ROOT.'/includes/template.php');
require_once(ENANO_ROOT.'/includes/plugins.php');
require_once(ENANO_ROOT.'/includes/lang.php');
-require_once(ENANO_ROOT.'/includes/comment.php');
-require_once(ENANO_ROOT.'/includes/wikiformat.php');
-require_once(ENANO_ROOT.'/includes/diff.php');
require_once(ENANO_ROOT.'/includes/render.php');
-require_once(ENANO_ROOT.'/includes/stats.php');
-require_once(ENANO_ROOT.'/includes/pageutils.php');
-require_once(ENANO_ROOT.'/includes/js-compressor.php');
require_once(ENANO_ROOT.'/includes/rijndael.php');
require_once(ENANO_ROOT.'/includes/email.php');
-require_once(ENANO_ROOT.'/includes/search.php');
-require_once(ENANO_ROOT.'/includes/json.php');
require_once(ENANO_ROOT.'/includes/json2.php');
-require_once(ENANO_ROOT.'/includes/math.php');
-require_once(ENANO_ROOT.'/includes/wikiengine/Tables.php');
require_once(ENANO_ROOT.'/includes/pageprocess.php');
require_once(ENANO_ROOT.'/includes/tagcloud.php');
@@ -198,6 +188,8 @@
// END BACKGROUND AND ENVIRONMENT CHECKS
//
+profiler_log('Background/environment checks done');
+
//
// MAIN API INITIALIZATION
//
@@ -308,7 +300,6 @@
<pre>
<?php
define("ENANO_ALLOW_LOAD_NOLANG", 1);
-$_GET["title"] = "langinstall";
require("includes/common.php");
install_language("eng", "English", "English", ENANO_ROOT . "/language/english/enano.json");</pre>');
}
--- a/includes/constants.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/constants.php Wed Jul 02 19:36:44 2008 -0400
@@ -76,6 +76,13 @@
define('IMAGE_TYPE_GIF', 2);
define('IMAGE_TYPE_JPG', 3);
+// token types
+define('TOKEN_VARIABLE', 1);
+define('TOKEN_BOOLOP', 2);
+define('TOKEN_PARENTHLEFT', 3);
+define('TOKEN_PARENTHRIGHT', 4);
+define('TOKEN_NOT', 5);
+
//
// User types - don't touch these
//
--- a/includes/dbal.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/dbal.php Wed Jul 02 19:36:44 2008 -0400
@@ -32,7 +32,10 @@
$debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
}
-
+
+global $db_sql_parse_time;
+$db_sql_parse_time = 0;
+
class mysql {
var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
var $row = array();
@@ -309,79 +312,46 @@
}
/**
- * Checks a SQL query for possible signs of injection attempts
+ * Performs heuristic analysis on a SQL query to check for known attack patterns.
* @param string $q the query to check
* @return bool true if query passed check, otherwise false
*/
function check_query($q, $debug = false)
{
- if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n";
- $sz = strlen($q);
- $quotechar = false;
- $quotepos = 0;
- $prev_is_quote = false;
- $just_started = false;
- for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
+ global $db_sql_parse_time;
+ $ts = microtime_float();
+
+ // remove properly escaped quotes
+ $q = str_replace(array("\\\"", "\\'"), '', $q);
+
+ // make sure quotes match
+ foreach ( array('"', "'") as $quote )
{
- $next = substr($q, $i+1, 1);
- $next2 = substr($q, $i+2, 1);
- $prev = substr($q, $i-1, 1);
- $prev2 = substr($q, $i-2, 1);
- if(isset($c) && in_array($c, Array('"', "'", '`')))
+ if ( get_char_count($q, $quote) % 2 == 1 )
{
- if($quotechar)
- {
- if (
- ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
- ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
- )
- {
- $quotechar = false;
- if($debug) echo('$db->check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />');
- $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
- if($debug) echo('$db->check_query(): Filtered query: '.$q.'<br />');
- $i = $quotepos;
- }
- }
- else
- {
- $quotechar = $c;
- $quotepos = $i;
- $just_started = true;
- }
- if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'<br />';
- continue;
- }
- $just_started = false;
- }
- if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
- for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
- {
- if (
- ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' )
- || ( in_array($c, Array('"', "'", '`')) )
- ) // Don't permit semicolons in mid-query, and never allow comments
- {
- // Injection attempt!
- if($debug)
- {
- $e = '';
- for($j=$i-5;$j<$i+5;$j++)
- {
- if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>';
- else $e .= $c;
- }
- echo 'Injection attempt caught at pos: '.$i.'<br />';
- }
+ // mismatched quotes
return false;
}
+ // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+ $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
}
+ $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+
+ // quotes are now matched out. does this string have a comment marker in it?
+ if ( strstr($q, '--') )
+ {
+ return false;
+ }
+
if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
{
if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
return false;
}
+
+ $ts = microtime_float() - $ts;
+ $db_sql_parse_time += $ts;
return true;
}
@@ -1066,72 +1036,39 @@
function check_query($q, $debug = false)
{
- if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n";
- $sz = strlen($q);
- $quotechar = false;
- $quotepos = 0;
- $prev_is_quote = false;
- $just_started = false;
- for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
+ global $db_sql_parse_time;
+ $ts = microtime_float();
+
+ // remove properly escaped quotes
+ $q = str_replace(array("\\\"", "\\'"), '', $q);
+
+ // make sure quotes match
+ foreach ( array('"', "'") as $quote )
{
- $next = substr($q, $i+1, 1);
- $next2 = substr($q, $i+2, 1);
- $prev = substr($q, $i-1, 1);
- $prev2 = substr($q, $i-2, 1);
- if(isset($c) && in_array($c, Array('"', "'", '`')))
+ if ( get_char_count($q, $quote) % 2 == 1 )
{
- if($quotechar)
- {
- if (
- ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
- ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
- )
- {
- $quotechar = false;
- if($debug) echo('$db->check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />');
- $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
- if($debug) echo('$db->check_query(): Filtered query: '.$q.'<br />');
- $i = $quotepos;
- }
- }
- else
- {
- $quotechar = $c;
- $quotepos = $i;
- $just_started = true;
- }
- if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'<br />';
- continue;
- }
- $just_started = false;
- }
- if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
- for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
- {
- if (
- ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' )
- || ( in_array($c, Array('"', "'", '`')) )
- ) // Don't permit semicolons in mid-query, and never allow comments
- {
- // Injection attempt!
- if($debug)
- {
- $e = '';
- for($j=$i-5;$j<$i+5;$j++)
- {
- if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>';
- else $e .= $c;
- }
- echo 'Injection attempt caught at pos: '.$i.'<br />';
- }
+ // mismatched quotes
return false;
}
+ // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+ $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
}
+ $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+
+ // quotes are now matched out. does this string have a comment marker in it?
+ if ( strstr($q, '--') )
+ {
+ return false;
+ }
+
if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
{
if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
return false;
}
+
+ $ts = microtime_float() - $ts;
+ $db_sql_parse_time += $ts;
return true;
}
--- a/includes/functions.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/functions.php Wed Jul 02 19:36:44 2008 -0400
@@ -4325,6 +4325,10 @@
foreach ( $profile as $i => $entry )
{
+ // $time_since_last = $entry['time'] - $time_last;
+ // if ( $time_since_last < 0.01 )
+ // continue;
+
$html .= "<!-- ########################################################## -->\n<tr>\n <th colspan=\"2\">Event $i</th>\n</tr>";
$html .= '<tr>' . "\n";
--- a/includes/pageprocess.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/pageprocess.php Wed Jul 02 19:36:44 2008 -0400
@@ -157,8 +157,6 @@
if ( !is_int($revision_id) )
$revision_id = 0;
- profiler_log("PageProcessor [{$namespace}:{$page_id}]: Ran initial checks");
-
$this->_setup( $page_id, $namespace, $revision_id );
}
@@ -172,7 +170,7 @@
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Started send process");
+ profiler_log('PageProcessor: send() called');
if ( !$this->perms->get_permissions('read') )
{
@@ -190,7 +188,6 @@
{
// Page isn't whitelisted, behave as normal
$this->err_access_denied();
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
return false;
}
}
@@ -237,7 +234,6 @@
if ( $this->password != $password )
{
$this->err_wrong_password();
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
return false;
}
}
@@ -245,6 +241,7 @@
}
if ( $this->page_exists && $this->namespace != 'Special' && $this->namespace != 'Admin' && $do_stats )
{
+ require_once(ENANO_ROOT.'/includes/stats.php');
doStats($this->page_id, $this->namespace);
}
if ( $this->namespace == 'Special' || $this->namespace == 'Admin' )
@@ -265,9 +262,7 @@
$func_name = "page_{$this->namespace}_{$this->page_id}";
if ( function_exists($func_name) )
{
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Calling special/admin page");
$result = @call_user_func($func_name);
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
return $result;
}
else
@@ -287,7 +282,6 @@
echo "<h2>$title</h2>
<p>$message</p>";
}
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
return false;
}
}
@@ -372,13 +366,11 @@
$template->init_vars($this);
}
- // die($this->page_id);
+ $text = $this->fetch_text();
- $text = $this->fetch_text();
if ( $text == 'err_no_text_rows' )
{
$this->err_no_rows();
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
return false;
}
else
@@ -409,7 +401,6 @@
}
}
}
- profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Finished send process");
}
/**
@@ -1029,6 +1020,7 @@
global $lang;
$text = $this->fetch_text();
+
$text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
$text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
@@ -1228,7 +1220,6 @@
}
else
{
-
$q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
. " LEFT JOIN " . table_prefix . "logs AS l\n"
. " ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
--- a/includes/paths.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/paths.php Wed Jul 02 19:36:44 2008 -0400
@@ -113,7 +113,7 @@
eval($cmd);
}
- $this->wiki_mode = (int)getConfig('wiki_mode')=='1';
+ $this->wiki_mode = ( getConfig('wiki_mode') == '1' ) ? 1 : 0;
$this->template_cache = Array();
}
function parse_url($sanitize = true)
--- a/includes/render.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/render.php Wed Jul 02 19:36:44 2008 -0400
@@ -217,6 +217,9 @@
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
+ require_once(ENANO_ROOT.'/includes/wikiformat.php');
+ require_once(ENANO_ROOT.'/includes/wikiengine/Tables.php');
+
profiler_log("RenderMan: starting wikitext render");
$random_id = md5( time() . mt_rand() );
@@ -474,13 +477,19 @@
/**
* Parses internal links (wikilinks) in a block of text.
* @param string Text to process
+ * @param string Optional. If included will be used as a template instead of using the default syntax.
* @return string
*/
- public static function parse_internal_links($text)
+ public static function parse_internal_links($text, $tplcode = false)
{
global $db, $session, $paths, $template, $plugins; // Common objects
+ if ( is_string($tplcode) )
+ {
+ $parser = $template->makeParserText($tplcode);
+ }
+
// stage 1 - links with alternate text
preg_match_all('/\[\[([^\[\]<>\{\}\|]+)\|(.+?)\]\]/', $text, $matches);
foreach ( $matches[0] as $i => $match )
@@ -493,7 +502,19 @@
$quot = '"';
$exists = ( isPage($pid_clean) ) ? '' : ' class="wikilink-nonexistent"';
- $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+ if ( $tplcode )
+ {
+ $parser->assign_vars(array(
+ 'HREF' => $url,
+ 'FLAGS' => $exists,
+ 'TEXT' => $inner_text
+ ));
+ $link = $parser->run();
+ }
+ else
+ {
+ $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+ }
$text = str_replace($match, $link, $text);
}
@@ -510,7 +531,19 @@
$quot = '"';
$exists = ( isPage($pid_clean) ) ? '' : ' class="wikilink-nonexistent"';
- $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+ if ( $tplcode )
+ {
+ $parser->assign_vars(array(
+ 'HREF' => $url,
+ 'FLAGS' => $exists,
+ 'TEXT' => $inner_text
+ ));
+ $link = $parser->run();
+ }
+ else
+ {
+ $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+ }
$text = str_replace($match, $link, $text);
}
@@ -845,6 +878,7 @@
public static function diff($str1, $str2)
{
global $db, $session, $paths, $template, $plugins; // Common objects
+ require_once(ENANO_ROOT.'/includes/diff.php');
$str1 = explode("\n", $str1);
$str2 = explode("\n", $str2);
$diff = new Diff($str1, $str2);
--- a/includes/sessions.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/sessions.php Wed Jul 02 19:36:44 2008 -0400
@@ -586,8 +586,8 @@
. ' ON g.group_id=m.group_id' . "\n"
. ' WHERE ( m.user_id='.$this->user_id.'' . "\n"
. ' OR g.group_name=\'Everyone\')' . "\n"
- . ' ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
- . ' ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden
+ . ' ' . ( /* quick hack for upgrade compatibility reasons */ enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
+ . ' ORDER BY group_id ASC;'); // The ORDER BY is to make sure "Everyone" comes first so the permissions can be overridden
if($row = $db->fetchrow())
{
do {
@@ -2801,6 +2801,8 @@
$objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache );
$object =& $objcache[$namespace][$page_id];
+ profiler_log("session: fetched ACLs for page {$namespace}:{$page_id}");
+
return $object;
}
@@ -3020,48 +3022,12 @@
// Cache the sitewide permissions for later use
$this->acl_base_cache = $this->perms;
- // Eliminate types that don't apply to this namespace
- foreach ( $this->perms AS $i => $perm )
- {
- if ( !in_array ( $paths->namespace, $this->acl_scope[$i] ) && !in_array('All', $this->acl_scope[$i]) )
- {
- unset($this->perms[$i]);
- }
- }
-
- // PAGE group info
- $pg_list = $paths->get_page_groups($paths->page_id, $paths->namespace);
- $pg_info = '';
- foreach ( $pg_list as $g_id )
- {
- $pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
- }
+ profiler_log('session: base ACL set calculated');
- // Build a query to grab ACL info
- $bs = 'SELECT rules,target_type,target_id FROM '.table_prefix.'acl WHERE ( ';
- $q = Array();
- $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
- if(count($this->groups) > 0)
- {
- foreach($this->groups as $g_id => $g_name)
- {
- $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
- }
- }
- // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
- // permissions to override group permissions.
- $bs .= implode(" OR\n ", $q) . " )\n AND (" . $pg_info . ' ( page_id=\''.$db->escape($paths->page_id).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) )
- ORDER BY target_type ASC, page_id ASC, namespace ASC;';
- $q = $this->sql($bs);
- if ( $row = $db->fetchrow() )
- {
- do {
- $rules = $this->string_to_perm($row['rules']);
- $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
- $this->acl_merge_with_current($rules, $is_everyone);
- } while ( $row = $db->fetchrow() );
- }
-
+ // Load and calculate permissions for the current page
+ $page_acl = $this->fetch_page_acl($paths->page_id, $paths->namespace);
+ $this->perms = $page_acl->perms;
+ $this->acl_defaults_used = $page_acl->acl_defaults_used;
}
/**
@@ -3558,6 +3524,8 @@
global $db, $session, $paths, $template, $plugins; // Common objects
// Setup EnanoMath and Diffie-Hellman
+ require_once(ENANO_ROOT.'/includes/math.php');
+
global $dh_supported;
$dh_supported = true;
try
--- a/includes/template.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/template.php Wed Jul 02 19:36:44 2008 -0400
@@ -86,23 +86,9 @@
// List out all CSS files for this theme
foreach ( $this->theme_list as $i => &$theme )
{
- $theme['css'] = array();
- $dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css";
- if ( $dh = @opendir($dir) )
- {
- while ( ( $file = @readdir($dh) ) !== false )
- {
- if ( preg_match('/\.css$/', $file) )
- $theme['css'][] = preg_replace('/\.css$/', '', $file);
- }
- closedir($dh);
- }
- // No CSS files? If so, nuke it.
- if ( count($theme['css']) < 1 )
- {
- unset($this->theme_list[$i]);
- }
+ $theme['css'] = $this->get_theme_css_files($theme['theme_id']);
}
+ unset($theme);
$this->theme_list = array_values($this->theme_list);
// Create associative array of themes
foreach ( $this->theme_list as $i => &$theme )
@@ -116,6 +102,33 @@
}
/**
+ * Gets the list of available CSS files (styles) for the specified theme.
+ * @param string Theme ID
+ * @return array
+ */
+
+ function get_theme_css_files($theme_id)
+ {
+ $css = array();
+ $dir = ENANO_ROOT . "/themes/{$theme_id}/css";
+ if ( $dh = @opendir($dir) )
+ {
+ while ( ( $file = @readdir($dh) ) !== false )
+ {
+ if ( preg_match('/\.css$/', $file) )
+ $css[] = preg_replace('/\.css$/', '', $file);
+ }
+ closedir($dh);
+ }
+ // No CSS files? If so, nuke it.
+ if ( count($css) < 1 )
+ {
+ unset($this->theme_list[$theme_id]);
+ }
+ return $css;
+ }
+
+ /**
* Failsafe constructor for upgrades.
*/
@@ -566,8 +579,6 @@
// PAGE TOOLBAR (on-page controls/actions)
//
- profiler_log('template: var init: finished initial setup, starting toolbar');
-
// Initialize the toolbar
$tb = '';
@@ -948,8 +959,6 @@
// OTHER SWITCHES
//
- profiler_log('template: var init: finshed toolbar, starting other switches');
-
$is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false;
$this->tpl_bool = Array(
@@ -1043,8 +1052,6 @@
$admin_link = $parser->run();
- profiler_log('template: var init: finished sidebar/misc processing, starting dynamic vars and finalization');
-
$SID = ($session->sid_super) ? $session->sid_super : '';
$urlname_clean = str_replace('\'', '\\\'', str_replace('\\', '\\\\', dirtify_page_id($local_fullpage)));
@@ -1111,8 +1118,6 @@
}
$js_dynamic .= "\n //]]>\n </script>";
- profiler_log('template: var init: finished JS dynamic vars and assigning final var set');
-
$tpl_strings = Array(
'PAGE_NAME'=>htmlspecialchars($local_cdata['name']),
'PAGE_URLNAME'=> $urlname_clean,
@@ -1154,14 +1159,14 @@
$this->assign_vars($tpl_strings, true);
+ profiler_log('template: var init: finished toolbar building and initial assign()');
+
//
// COMPILE THE SIDEBAR
//
// This is done after the big assign_vars() so that sidebar code has access to the newly assigned variables
- profiler_log('template: var init: finished final var set, executing and applying sidebar templates');
-
list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar();
$this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != $min) ? true : false;
$this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false;
@@ -1299,7 +1304,11 @@
$t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
if ( defined('ENANO_DEBUG') )
+ {
$t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
+ // ob_end_clean();
+ // return profiler_make_html();
+ }
return $t;
}
@@ -1381,8 +1390,28 @@
$this->init_vars();
}
+ $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $file) . '.php';
+ if ( file_exists($cache_file) )
+ {
+ // this is about the time of the breaking change to cache file format
+ if ( filemtime($cache_file) > 1215038089 )
+ {
+ $result = @include($cache_file);
+ if ( isset($md5) )
+ {
+ if ( $md5 == md5_file(ENANO_ROOT . "/themes/{$this->theme}/$file") )
+ {
+ $result = $this->compile_template_text_post($result);
+ return $result;
+ }
+ }
+ }
+ }
+
$compiled = $this->compile_template($file);
- return eval($compiled);
+ $result = eval($compiled);
+
+ return $result;
}
/**
@@ -1475,30 +1504,7 @@
</p>');
}
- // Check for cached copy
- // This will make filenames in the pattern of theme-file.tpl.php
- $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
-
- // Only use cached copy if caching is enabled
- // (it is enabled by default I think)
- if ( file_exists($cache_file) && getConfig('cache_thumbs') == '1' )
- {
- // Cache files are auto-generated, but otherwise are normal PHP files
- include($cache_file);
-
- // Fetch content of the ORIGINAL
- $text = file_get_contents($tpl_file_fullpath);
-
- // $md5 will be set by the cached file
- // This makes sure that a cached copy of the template is used only if its MD5
- // matches the MD5 of the file that the compiled file was compiled from.
- if ( isset($md5) && $md5 == md5($text) )
- {
- return $this->compile_template_text_post(str_replace('\\"', '"', $tpl_text));
- }
- }
-
- // We won't use the cached copy here
+ // We won't use the cached copy here.
$text = file_get_contents($tpl_file_fullpath);
// This will be used later when writing the cached file
@@ -1507,6 +1513,9 @@
// Preprocessing and checks complete - compile the code
$text = $this->compile_tpl_code($text);
+ // Generate cache filename
+ $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
+
// Perhaps caching is enabled and the admin has changed the template?
if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' )
{
@@ -1517,19 +1526,22 @@
return $text;
}
- // Escape the compiled code so it can be eval'ed
- $text_escaped = addslashes($text);
- $notice = <<<EOF
+ // Final contents of cache file
+ $file_contents = <<<EOF
+<?php
/*
* NOTE: This file was automatically generated by Enano and is based on compiled code. Do not edit this file.
* If you edit this file, any changes you make will be lost the next time the associated source template file is edited.
*/
+\$md5 = '$md5';
+
+$text
EOF;
// This is really just a normal PHP file that sets a variable or two and exits.
// $tpl_text actually will contain the compiled code
- fwrite($h, '<?php ' . $notice . ' $md5 = \'' . $md5 . '\'; $tpl_text = \'' . $text_escaped . '\'; ?>');
+ fwrite($h, $file_contents);
fclose($h);
}
@@ -1613,6 +1625,26 @@
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
+ $START = microtime_float();
+
+ // localize the whole string first
+ preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches);
+ foreach ( $matches[1] as $i => $string_id )
+ {
+ $string = $lang->get($string_id);
+ $string = str_replace('\\', '\\\\', $string);
+ $string = str_replace('\'', '\\\'', $string);
+ $message = str_replace_once($matches[0][$i], $string, $message);
+ }
+
+ // first: the hackish optimization -
+ // if it's only a bunch of letters, numbers and spaces, just skip this sh*t.
+
+ if ( preg_match('/^[\w\s\.]*$/i', $message) )
+ {
+ return $message;
+ }
+
$filter_links = false;
$tplvars = $this->extract_vars($filename);
if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
@@ -1636,185 +1668,45 @@
// Conditionals
- preg_match_all('#\{if ([A-Za-z0-9_ \(\)&\|\!-]*)\}(.*?)\{\/if\}#is', $message, $links);
-
- // Temporary exception from coding standards - using tab length of 4 here for clarity
- for ( $i = 0; $i < sizeof($links[1]); $i++ )
- {
- $condition =& $links[1][$i];
- $message = str_replace('{if '.$condition.'}'.$links[2][$i].'{/if}', '{CONDITIONAL:'.$i.':'.$random_id.'}', $message);
-
- // Time for some manual parsing...
- $chk = false;
- $current_id = '';
- $prn_level = 0;
- // Used to keep track of where we are in the conditional
- // Object of the game: turn {if this && ( that OR !something_else )} ... {/if} into if( ( isset($this->tpl_bool['that']) && $this->tpl_bool['that'] ) && ...
- // Method of attack: escape all variables, ignore all else. Non-valid code is filtered out by a regex above.
- $in_var_now = true;
- $in_var_last = false;
- $current_var = '';
- $current_var_start_pos = 0;
- $current_var_end_pos = 0;
- $j = -1;
- $condition = $condition . ' ';
- $d = strlen($condition);
- while($j < $d)
- {
- $j++;
- $in_var_last = $in_var_now;
-
- $char = substr($condition, $j, 1);
- $in_var_now = ( preg_match('#^([A-z0-9_]*){1}$#', $char) ) ? true : false;
- if(!$in_var_last && $in_var_now)
- {
- $current_var_start_pos = $j;
- }
- if($in_var_last && !$in_var_now)
- {
- $current_var_end_pos = $j;
- }
- if($in_var_now)
- {
- $current_var .= $char;
- continue;
- }
- // 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.
- 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')
- {
- // XSS attack! Bail out
- $errmsg = '<p><b>Error:</b> Syntax error (possibly XSS attack) caught in template code:</p>';
- $errmsg .= '<pre>';
- $errmsg .= '{if '.htmlspecialchars($condition).'}';
- $errmsg .= "\n ";
- for ( $k = 0; $k < $j; $k++ )
- {
- $errmsg .= " ";
- }
- // Show position of error
- $errmsg .= '<span style="color: red;">^</span>';
- $errmsg .= '</pre>';
- $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $errmsg, $message);
- continue 2;
- }
- if($current_var != '')
- {
- $cd = '( isset($this->tpl_bool[\''.$current_var.'\']) && $this->tpl_bool[\''.$current_var.'\'] )';
- $cvt = substr($condition, 0, $current_var_start_pos) . $cd . substr($condition, $current_var_end_pos, strlen($condition));
- $j = $j + strlen($cd) - strlen($current_var);
- $current_var = '';
- $condition = $cvt;
- $d = strlen($condition);
- }
- }
- $condition = substr($condition, 0, strlen($condition)-1);
- $condition = '$chk = ( '.$condition.' ) ? true : false;';
- eval($condition);
-
- if($chk)
- {
- if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}'));
- else $c = $links[2][$i];
- $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
- }
- else
- {
- if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i]));
- else $c = '';
- $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
- }
- }
-
- preg_match_all('#\{!if ([A-Za-z_-]*)\}(.*?)\{\/if\}#is', $message, $links);
-
- for($i=0;$i<sizeof($links[1]);$i++)
- {
- $message = str_replace('{!if '.$links[1][$i].'}'.$links[2][$i].'{/if}', '{CONDITIONAL:'.$i.':'.$random_id.'}', $message);
- if(isset($this->tpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]]) {
- if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i]));
- else $c = '';
- $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
- } else {
- if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}'));
- else $c = $links[2][$i];
- $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message);
- }
- }
-
- preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches);
- foreach ( $matches[1] as $i => $string_id )
- {
- $string = $lang->get($string_id);
- $string = str_replace('\\', '\\\\', $string);
- $string = str_replace('\'', '\\\'', $string);
- $message = str_replace_once($matches[0][$i], $string, $message);
- }
+ $message = $this->twf_parse_conditionals($message);
/*
* HTML RENDERER
*/
// Images
- $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $message, $matchlist);
- $matches = Array();
- $matches['images'] = $matchlist[1];
- for($i=0;$i<sizeof($matchlist[1]);$i++)
- {
- if(isPage($paths->nslist['File'].$matches['images'][$i]))
- {
- $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]',
- '<img alt="'.$matches['images'][$i].'" style="border: 0" src="'.makeUrlNS('Special', 'DownloadFile/'.$matches['images'][$i]).'" />',
- $message);
- }
- }
+ $message = RenderMan::process_image_tags($message, $taglist);
+ $message = RenderMan::process_imgtags_stage2($message, $taglist);
// Internal links
-
- $text_parser = $this->makeParserText($tplvars['sidebar_button']);
-
- preg_match_all("#\[\[([^\|\]\n\a\r\t]*?)\]\]#is", $message, $il);
- for($i=0;$i<sizeof($il[1]);$i++)
- {
- $href = makeUrl(str_replace(' ', '_', $il[1][$i]), null, true);
- $text_parser->assign_vars(Array(
- 'HREF' => $href,
- 'FLAGS' => '',
- 'TEXT' => $il[1][$i]
- ));
- $message = str_replace("[[{$il[1][$i]}]]", $text_parser->run(), $message);
- }
-
- preg_match_all('#\[\[([^\|\]\n\a\r\t]*?)\|([^\]\r\n\a\t]*?)\]\]#is', $message, $il);
- for($i=0;$i<sizeof($il[1]);$i++)
- {
- $href = makeUrl(str_replace(' ', '_', $il[1][$i]), null, true);
- $text_parser->assign_vars(Array(
- 'HREF' => $href,
- 'FLAGS' => '',
- 'TEXT' => $il[2][$i]
- ));
- $message = str_replace("[[{$il[1][$i]}|{$il[2][$i]}]]", $text_parser->run(), $message);
- }
+ $message = RenderMan::parse_internal_links($message, $tplvars['sidebar_button']);
// External links
- // $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?) ([^\]]+)\\]#', '<a href="\\1://\\2">\\3</a><br style="display: none;" />', $message);
- // $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\]#', '<a href="\\1://\\2">\\1://\\2</a><br style="display: none;" />', $message);
- preg_match_all('/\[((https?|ftp|irc):\/\/([^@\s\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?) ([^\]]+)\]/is', $message, $ext_link);
-
- // die('<pre>' . htmlspecialchars( print_r($ext_link, true) ) . '</pre>');
+ $url_regexp = <<<EOF
+(
+ (?:https?|ftp|irc):\/\/ # protocol
+ (?:[^@\s\]"\':]+@)? # username (FTP only but whatever)
+ (?:(?:(?:[a-z0-9-]+\.)*)[a-z0-9-]+) # hostname
+ (?:\/[A-z0-9_%\|~`!\!@#\$\^&?=\*\(\):;\.,\/-]*)? # path
+)
+EOF;
+
+ $text_parser = $this->makeParserText($tplvars['sidebar_button']);
+
+ preg_match_all('/\[' . $url_regexp . '[ ]([^\]]+)\]/isx', $message, $ext_link);
for ( $i = 0; $i < count($ext_link[0]); $i++ )
{
$text_parser->assign_vars(Array(
'HREF' => $ext_link[1][$i],
'FLAGS' => '',
- 'TEXT' => $ext_link[16][$i]
+ 'TEXT' => $ext_link[2][$i]
));
$message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
}
- preg_match_all('/\[((https?|ftp|irc):\/\/([^@\s\]"\':]+)?((([a-z0-9-]+\.)*)[a-z0-9-]+)(\/[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]*(\?(([a-z0-9_-]+)(=[A-z0-9_%\|~`\!@#\$\^&\*\(\):;\.,\/-\[\]]+)?((&([a-z0-9_-]+)(=[A-z0-9_%\|~`!\!@#\$\^&\*\(\):;\.,\/-]+)?)*))?)?)?)\]/is', $message, $ext_link);
+ preg_match_all('/\[' . $url_regexp . '\]/is', $message, $ext_link);
for ( $i = 0; $i < count($ext_link[0]); $i++ )
{
@@ -1826,34 +1718,182 @@
$message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
}
- $parser1 = $this->makeParserText($tplvars['sidebar_section']);
- $parser2 = $this->makeParserText($tplvars['sidebar_section_raw']);
-
- preg_match_all('#\{slider(2|)=([^\}]*?)\}(.*?)\{\/slider(2|)\}#is', $message, $sb);
-
- // Modified to support the sweet new template var system
- for($i=0;$i<sizeof($sb[1]);$i++)
- {
- $p = ($sb[1][$i] == '2') ? $parser2 : $parser1;
- $p->assign_vars(Array('TITLE'=>$sb[2][$i],'CONTENT'=>$sb[3][$i]));
- $message = str_replace("{slider{$sb[1][$i]}={$sb[2][$i]}}{$sb[3][$i]}{/slider{$sb[4][$i]}}", $p->run(), $message);
- }
+ $TIME = microtime_float() - $START;
/*
- Extras ;-)
- $message = preg_replace('##is', '', $message);
- $message = preg_replace('##is', '', $message);
- $message = preg_replace('##is', '', $message);
- $message = preg_replace('##is', '', $message);
- $message = preg_replace('##is', '', $message);
+ if ( $TIME > 0.02 )
+ {
+ echo 'template: tplWikiFormat took a while for this one. string dump:<pre>';
+ echo htmlspecialchars($message);
+ echo '</pre>';
+ }
*/
- //die('<pre>'.htmlspecialchars($message).'</pre>');
- //eval($message); exit;
+ return $message;
+ }
+
+ /**
+ * Parses conditional {if} blocks in sidebars and other tplWikiFormatted things
+ * @param string A string potentially containing conditional blocks
+ * @return string Processed string
+ */
+
+ function twf_parse_conditionals($message)
+ {
+ if ( !preg_match_all('/\{(!?)if ([a-z0-9_\(\)\|&! ]+)\}(.*?)(?:\{else\}(.*?))?\{\/if\}/is', $message, $matches) )
+ {
+ return $message;
+ }
+ foreach ( $matches[0] as $match_id => $full_block )
+ {
+ // 1 = "not" flag
+ // 2 = condition
+ // 3 = if true
+ // 4 = else
+ $condresult = $this->process_condition($matches[2][$match_id]);
+ if ( !empty($matches[1][$match_id]) )
+ {
+ if ( $condresult == 1 )
+ $condresult = 2;
+ else if ( $condresult == 2 )
+ $condresult = 1;
+ }
+ switch($condresult)
+ {
+ case 1:
+ // evaluated to false
+ $message = str_replace_once($full_block, $matches[4][$match_id], $message);
+ break;
+ case 2:
+ // evaluated to true
+ $message = str_replace_once($full_block, $matches[3][$match_id], $message);
+ break;
+ case 3:
+ $message = str_replace_once($full_block, "Syntax error: mismatched parentheses (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+ break;
+ case 4:
+ $message = str_replace_once($full_block, "Syntax error: illegal character (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+ break;
+ case 5:
+ $message = str_replace_once($full_block, "Syntax error: illegal sequence (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+ break;
+ }
+ }
return $message;
}
/**
+ * Inner-loop parser for a conditional block. Verifies a string condition to make sure it's syntactically correct, then returns what it evaluates to.
+ * Return values:
+ * 1 - string evaluates to true
+ * 2 - string evaluates to false
+ * 3 - Syntax error - mismatched parentheses
+ * 4 - Syntax error - unknown token
+ * 5 - Syntax error - invalid sequence
+ * @param string
+ * @return int
+ *
+ */
+
+ function process_condition($condition)
+ {
+ // make sure parentheses are matched
+ $parentheses = preg_replace('/[^\(\)]/', '', $condition);
+ if ( !empty($parentheses) )
+ {
+ $i = 0;
+ $parentheses = enano_str_split($parentheses);
+ foreach ( $parentheses as $chr )
+ {
+ $inc = ( $chr == '(' ) ? 1 : -1;
+ $i += $inc;
+ }
+ if ( $i != 0 )
+ {
+ // mismatched parentheses
+ return 3;
+ }
+ }
+ // sequencer check
+ // first, pad all sequences of characters with spaces
+ $seqcheck = preg_replace('/([a-z0-9_]+)/i', '\\1 ', $condition);
+ $seqcheck = preg_replace('/([&|()!])/i', '\\1 ', $seqcheck);
+ // now shrink all spaces to one space each
+ $seqcheck = preg_replace('/[ ]+/', ' ', $seqcheck);
+
+ // explode it. the allowed sequences are:
+ // - TOKEN_NOT + TOKEN_VARIABLE
+ // - TOKEN_NOT + TOKEN_PARENTHLEFT
+ // - TOKEN_BOOLOP + TOKEN_NOT
+ // - TOKEN_PARENTHRIGHT + TOKEN_NOT
+ // - TOKEN_VARIABLE + TOKEN_BOOLOP
+ // - TOKEN_BOOLOP + TOKEN_PARENTHLEFT
+ // - TOKEN_PARENTHLEFT + TOKEN_VARIABLE
+ // - TOKEN_BOOLOP + TOKEN_VARIABLE
+ // - TOKEN_VARIABLE + TOKEN_PARENTHRIGHT
+ // - TOKEN_PARENTHRIGHT + TOKEN_BOOLOP
+ $seqcheck = explode(' ', trim($seqcheck));
+ $last_item = TOKEN_BOOLOP;
+ foreach ( $seqcheck as $i => $token )
+ {
+ // determine type
+ if ( $token == '(' )
+ {
+ $type = TOKEN_PARENTHLEFT;
+ }
+ else if ( $token == ')' )
+ {
+ $type = TOKEN_PARENTHRIGHT;
+ }
+ else if ( $token == '!' )
+ {
+ $type = TOKEN_NOT;
+ }
+ else if ( strtolower($token) == 'and' || strtolower($token) == 'or' || $token == '&&' || $token == '||' )
+ {
+ $type = TOKEN_BOOLOP;
+ }
+ else if ( preg_match('/^[a-z0-9_]+$/i', $token) )
+ {
+ $type = TOKEN_VARIABLE;
+ // at this point it's considered safe to wrap it
+ $seqcheck[$i] = "( isset(\$this->tpl_bool['$token']) && \$this->tpl_bool['$token'] )";
+ }
+ else
+ {
+ // syntax error - doesn't match known token types
+ return 4;
+ }
+ // inner sequence check
+ if (
+ ( $last_item == TOKEN_BOOLOP && $type == TOKEN_NOT ) ||
+ ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_NOT ) ||
+ ( $last_item == TOKEN_NOT && $type == TOKEN_VARIABLE ) ||
+ ( $last_item == TOKEN_NOT && $type == TOKEN_PARENTHLEFT ) ||
+ ( $last_item == TOKEN_VARIABLE && $type == TOKEN_BOOLOP ) ||
+ ( $last_item == TOKEN_BOOLOP && $type == TOKEN_PARENTHLEFT ) ||
+ ( $last_item == TOKEN_PARENTHLEFT && $type == TOKEN_VARIABLE ) ||
+ ( $last_item == TOKEN_BOOLOP && $type == TOKEN_VARIABLE ) ||
+ ( $last_item == TOKEN_VARIABLE && $type == TOKEN_PARENTHRIGHT ) ||
+ ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_BOOLOP )
+ )
+ {
+ // sequence is good, continue
+ }
+ else
+ {
+ // sequence is invalid, break out
+ return 5;
+ }
+ $last_item = $type;
+ }
+ // passed all checks
+ $seqcheck = implode(' ', $seqcheck);
+ $result = eval("return ( $seqcheck ) ? true : false;");
+ return ( $result ) ? 2 : 1;
+ }
+
+ /**
* Print a text field that auto-completes a username entered into it.
* @param string $name - the name of the form field
* @return string
@@ -1977,6 +2017,25 @@
$left = '';
$right = '';
+ // check the cache
+ $cache_enable = getConfig('cache_thumbs') == '1' && !$session->user_logged_in;
+ $cache_file = ENANO_ROOT . "/cache/cache_anon_sidebar.php";
+ $cache_fresh = intval(getConfig('sidebar_anon_cache_time') + 600) >= time();
+ if ( $cache_enable && $cache_fresh )
+ {
+ @include($cache_file);
+ if ( isset($sidebar_cache) )
+ {
+ // we loaded the cache!
+ foreach ( $sidebar_cache as $i => $_ )
+ {
+ $block =& $sidebar_cache[$i];
+ $block = str_replace('$USERNAME$', $session->username, $block);
+ }
+ return $sidebar_cache;
+ }
+ }
+
if ( !$this->fetch_block('Links') )
$this->initLinksWidget();
@@ -1988,9 +2047,11 @@
if(isset($vars['sidebar_top']))
{
- $left .= $this->parse($vars['sidebar_top']);
- $right .= $this->parse($vars['sidebar_top']);
+ $top = $this->parse($vars['sidebar_top']);
+ $left .= $top;
+ $right .= $top;
}
+
while($row = $db->fetchrow())
{
switch($row['block_type'])
@@ -2033,19 +2094,42 @@
$db->free_result();
if(isset($vars['sidebar_bottom']))
{
- $left .= $this->parse($vars['sidebar_bottom']);
- $right .= $this->parse($vars['sidebar_bottom']);
+ $bottom = $this->parse($vars['sidebar_bottom']);
+ $left .= $bottom;
+ $right .= $bottom;
}
$min = '';
if(isset($vars['sidebar_top']))
{
- $min .= $this->parse($vars['sidebar_top']);
+ $min .= $top;
}
if(isset($vars['sidebar_bottom']))
{
- $min .= $this->parse($vars['sidebar_bottom']);
+ $min .= $bottom;
}
- return Array($left, $right, $min);
+ $return = Array($left, $right, $min);
+ if ( $cache_enable )
+ {
+ $cachestore = Language::var_export_string($return);
+ $cachestore = str_replace($session->username, '$USERNAME$', $cachestore);
+ $cachestore = <<<EOF
+<?php
+/**
+ * Automatically generated cache of the sidebar for guests.
+ * Do not modify this, it is refreshed every 15 minutes.
+ */
+
+\$sidebar_cache = $cachestore;
+EOF;
+ $fh = @fopen($cache_file, 'w');
+ if ( $fh )
+ {
+ fwrite($fh, $cachestore);
+ fclose($fh);
+ }
+ setConfig('sidebar_anon_cache_time', time());
+ }
+ return $return;
}
function initLinksWidget()
@@ -2053,6 +2137,7 @@
global $db, $session, $paths, $template, $plugins; // Common objects
// SourceForge/W3C buttons
$ob = Array();
+ // FIXME: l10n
$admintitle = ( $session->user_level >= USER_LEVEL_ADMIN ) ? 'title="You may disable this button in the admin panel under General Configuration."' : '';
if(getConfig('sflogo_enabled')=='1')
{
@@ -2676,11 +2761,8 @@
function process_template($file)
{
- profiler_log("[template_nodb] STARTED eval of file $file");
$compiled = $this->compile_template($file);
- profiler_log("[template_nodb] COMPILED file $file");
$result = eval($compiled);
- profiler_log("[template_nodb] FINISHED eval of file $file");
return $result;
}
--- a/index.php Mon Jun 30 17:22:29 2008 -0400
+++ b/index.php Wed Jul 02 19:36:44 2008 -0400
@@ -19,7 +19,7 @@
define('ENANO_INTERFACE_INDEX', '');
// For the mighty and brave.
- // define('ENANO_DEBUG', '');
+ define('ENANO_DEBUG', '');
// Set up gzip encoding before any output is sent
@@ -65,6 +65,7 @@
break;
case 'comments':
$template->header();
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$sub = ( isset ($_GET['sub']) ) ? $_GET['sub'] : false;
switch($sub)
{
@@ -121,6 +122,7 @@
redirect(makeUrl($paths->page), '', '', 0);
break;
}
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(isset($_POST['_save']))
{
$captcha_valid = true;
@@ -258,6 +260,7 @@
$template->footer();
break;
case 'history':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$hist = PageUtils::histlist($paths->page_id, $paths->namespace);
$template->header();
echo $hist;
@@ -286,6 +289,7 @@
$template->footer();
break;
case 'catedit':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(isset($_POST['__enanoSaveButton']))
{
unset($_POST['__enanoSaveButton']);
@@ -313,6 +317,7 @@
break;
case 'protect':
if (!isset($_REQUEST['level'])) die_friendly('Invalid request', '<p>No protection level specified</p>');
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(!empty($_POST['reason']))
{
if(!preg_match('#^([0-2]*){1}$#', $_POST['level'])) die_friendly('Error protecting page', '<p>Request validation failed</p>');
@@ -349,6 +354,7 @@
$template->footer();
break;
case 'rename':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(!empty($_POST['newname']))
{
$r = PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newname']);
@@ -370,6 +376,7 @@
{
die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
}
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(isset($_POST['_downthejohn']))
{
$template->header();
@@ -388,6 +395,7 @@
$template->footer();
break;
case 'delvote':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(isset($_POST['_ballotbox']))
{
$template->header();
@@ -416,6 +424,7 @@
$template->footer();
break;
case 'resetvotes':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(!$session->get_permissions('vote_reset'))
{
die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
@@ -442,6 +451,7 @@
{
die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
}
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
if(isset($_POST['_adiossucker']))
{
$reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
@@ -509,6 +519,8 @@
}
break;
case 'diff':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
+ require_once(ENANO_ROOT.'/includes/diff.php');
$template->header();
$id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
$id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
@@ -533,6 +545,7 @@
die_friendly($lang->get('page_detag_success_title'), '<p>' . $lang->get('page_detag_success_body') . '</p>');
break;
case 'aclmanager':
+ require_once(ENANO_ROOT.'/includes/pageutils.php');
$data = ( isset($_POST['data']) ) ? $_POST['data'] : Array('mode' => 'listgroups');
PageUtils::aclmanager($data);
break;
--- a/plugins/SpecialSearch.php Mon Jun 30 17:22:29 2008 -0400
+++ b/plugins/SpecialSearch.php Wed Jul 02 19:36:44 2008 -0400
@@ -65,6 +65,8 @@
global $aggressive_optimize_html;
global $lang;
+ require_once(ENANO_ROOT.'/includes/search.php');
+
$aggressive_optimize_html = false;
if ( !$q = $paths->getParam(0) )