Improved ban control page and allowed multiple entries/IP ranges; changed some parameters on jBox; user level changes are logged now
--- a/includes/clientside/static/dropdown.js Sat Sep 08 15:06:28 2007 -0400
+++ b/includes/clientside/static/dropdown.js Sat Sep 08 22:58:38 2007 -0400
@@ -123,7 +123,7 @@
if(typeof(others[i]) == 'object')
{
others[i].style.display = 'none';
- others[i].previousSibling.className = '';
+ $(others[i].previousSibling).rmClass('liteselected');
}
}
var others = obj.parentNode.getElementsByTagName('div');
@@ -134,13 +134,14 @@
if ( others[i].className == 'submenu' )
{
others[i].style.display = 'none';
- others[i].previousSibling.className = '';
+ $(others[i].previousSibling).rmClass('liteselected');
}
}
}
if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' ))
{
- obj.className = 'liteselected';
+ $(a).addClass('liteselected');
+ //obj.className = 'liteselected';
var ul = obj.nextSibling;
var dim = fetch_dimensions(obj);
var off = fetch_offset(obj);
@@ -197,7 +198,7 @@
if (!isOverObj(a, false, event) && !isOverObj(ul, true, event))
{
- a.className = '';
+ $(a).rmClass('liteselected');
if ( jBox_slide_enable )
{
@@ -399,7 +400,7 @@
{
if ( !isOverObj(uls[j], false, e) )
{
- uls[j].previousSibling.className = '';
+ $(uls[j].previousSibling).rmClass('liteselected');
//uls[j].style.display = 'none';
slideIn(uls[j]);
}
@@ -412,7 +413,7 @@
{
if ( !isOverObj(uls[j], false, e) )
{
- uls[j].previousSibling.className = '';
+ $(uls[j].previousSibling).rmClass('liteselected');
//uls[j].style.display = 'none';
slideIn(uls[j]);
}
--- a/includes/clientside/static/toolbar.js Sat Sep 08 15:06:28 2007 -0400
+++ b/includes/clientside/static/toolbar.js Sat Sep 08 22:58:38 2007 -0400
@@ -9,7 +9,7 @@
{
if(obj.id == 'mdgToolbar_article' || obj.id == 'mdgToolbar_discussion')
{
- obj.className = '';
+ $(obj).rmClass('selected');
}
obj = obj.nextSibling;
}
@@ -22,7 +22,7 @@
obj = document.getElementById('pagebar_main').firstChild.nextSibling;
while(obj)
{
- if ( obj.className != 'selected' )
+ if ( !$(obj).hasClass('selected') )
{
obj = obj.nextSibling;
continue;
@@ -30,7 +30,7 @@
if(obj.id != 'mdgToolbar_article' && obj.id != 'mdgToolbar_discussion')
{
if ( obj.className )
- obj.className = '';
+ $(obj).rmClass('selected');
}
obj = obj.nextSibling;
}
@@ -46,7 +46,7 @@
if(typeof(dom) == 'object')
{
unselectAllButtonsMajor();
- document.getElementById('mdgToolbar_'+which).className = 'selected';
+ $('mdgToolbar_'+which).addClass('selected');
}
}
@@ -57,7 +57,7 @@
if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object')
{
unselectAllButtonsMinor();
- document.getElementById('mdgToolbar_'+which).className = 'selected';
+ $('mdgToolbar_'+which).addClass('selected');
}
}
--- a/includes/dbal.php Sat Sep 08 15:06:28 2007 -0400
+++ b/includes/dbal.php Sat Sep 08 22:58:38 2007 -0400
@@ -196,7 +196,7 @@
$quotepos = 0;
$prev_is_quote = false;
$just_started = false;
- for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
+ for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
{
$next = substr($q, $i+1, 1);
$next2 = substr($q, $i+2, 1);
@@ -206,8 +206,8 @@
{
if($quotechar)
{
- if(
- ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_entered ) && $prev != '\\') ||
+ if (
+ ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
)
{
@@ -222,17 +222,20 @@
{
$quotechar = $c;
$quotepos = $i;
- $just_entered = true;
+ $just_started = true;
}
if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'<br />';
continue;
}
- $just_entered = false;
+ $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) == '--') // Don't permit semicolons in mid-query, and never allow comments
+ 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)
@@ -248,6 +251,11 @@
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;
+ }
return true;
}
--- a/includes/functions.php Sat Sep 08 15:06:28 2007 -0400
+++ b/includes/functions.php Sat Sep 08 22:58:38 2007 -0400
@@ -2846,6 +2846,85 @@
return $html;
}
+/**
+ * For an input range of numbers (like 25-256) returns an array filled with all numbers in the range, inclusive.
+ * @param string
+ * @return array
+ */
+
+function int_range($range)
+{
+ if ( strval(intval($range)) == $range )
+ return $range;
+ if ( !preg_match('/^[0-9]+(-[0-9]+)?$/', $range) )
+ return false;
+ $ends = explode('-', $range);
+ if ( count($ends) != 2 )
+ return $range;
+ $ret = array();
+ if ( $ends[1] < $ends[0] )
+ $ends = array($ends[1], $ends[0]);
+ else if ( $ends[0] == $ends[1] )
+ return array($ends[0]);
+ for ( $i = $ends[0]; $i <= $ends[1]; $i++ )
+ {
+ $ret[] = $i;
+ }
+ return $ret;
+}
+
+/**
+ * Parses a range or series of IP addresses, and returns the raw addresses. Only parses ranges in the last two octets to prevent DOSing.
+ * Syntax for ranges: x.x.x.x; x|y.x.x.x; x.x.x-z.x; x.x.x-z|p.q|y
+ * @param string IP address range string
+ * @return array
+ */
+
+function parse_ip_range($range)
+{
+ $octets = explode('.', $range);
+ if ( count($octets) != 4 )
+ // invalid range
+ return $range;
+ $i = 0;
+ $possibilities = array( 0 => array(), 1 => array(), 2 => array(), 3 => array() );
+ foreach ( $octets as $octet )
+ {
+ $existing =& $possibilities[$i];
+ $inner = explode('|', $octet);
+ foreach ( $inner as $bit )
+ {
+ if ( $i >= 2 )
+ {
+ $bits = int_range($bit);
+ if ( $bits === false )
+ return false;
+ else if ( !is_array($bits) )
+ $existing[] = intval($bits);
+ else
+ $existing = array_merge($existing, $bits);
+ }
+ else
+ {
+ $bit = intval($bit);
+ $existing[] = $bit;
+ }
+ }
+ $existing = array_unique($existing);
+ $i++;
+ }
+ $ips = array();
+
+ // The only way to combine all those possibilities. ;-)
+ foreach ( $possibilities[0] as $oc1 )
+ foreach ( $possibilities[1] as $oc2 )
+ foreach ( $possibilities[2] as $oc3 )
+ foreach ( $possibilities[3] as $oc4 )
+ $ips[] = "$oc1.$oc2.$oc3.$oc4";
+
+ return $ips;
+}
+
//die('<pre>Original: 01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'</pre>');
?>
--- a/includes/template.php Sat Sep 08 15:06:28 2007 -0400
+++ b/includes/template.php Sat Sep 08 22:58:38 2007 -0400
@@ -943,7 +943,15 @@
$h = fopen($tpl_filename, 'w');
if(!$h) return $text;
$t = addslashes($text);
- fwrite($h, '<?php $md5 = \''.$md5.'\'; $tpl_text = \''.$t.'\'; ?>');
+ $notice = <<<EOF
+
+/*
+ * 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.
+ */
+
+EOF;
+ fwrite($h, '<?php ' . $notice . ' $md5 = \''.$md5.'\'; $tpl_text = \''.$t.'\'; ?>');
fclose($h);
}
return $text; //('<pre>'.htmlspecialchars($text).'</pre>');
--- a/plugins/SpecialAdmin.php Sat Sep 08 15:06:28 2007 -0400
+++ b/plugins/SpecialAdmin.php Sat Sep 08 22:58:38 2007 -0400
@@ -860,19 +860,31 @@
// We need to update group memberships
if ( $old_level == USER_LEVEL_ADMIN )
{
+ $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","u_from_admin",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_POST['new_username']) . '");');
+ if ( !$q )
+ $db->_die();
$session->remove_user_from_group($user_id, GROUP_ID_ADMIN);
}
else if ( $old_level == USER_LEVEL_MOD )
{
+ $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","u_from_mod",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_POST['new_username']) . '");');
+ if ( !$q )
+ $db->_die();
$session->remove_user_from_group($user_id, GROUP_ID_MOD);
}
if ( $new_level == USER_LEVEL_ADMIN )
{
+ $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","u_to_admin",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_POST['new_username']) . '");');
+ if ( !$q )
+ $db->_die();
$session->add_user_to_group($user_id, GROUP_ID_ADMIN, false);
}
else if ( $new_level == USER_LEVEL_MOD )
{
+ $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","u_to_mod",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_POST['new_username']) . '");');
+ if ( !$q )
+ $db->_die();
$session->add_user_to_group($user_id, GROUP_ID_MOD, false);
}
}
@@ -2064,12 +2076,66 @@
}
if(isset($_POST['create']) && !defined('ENANO_DEMO_MODE'))
{
- $q = 'INSERT INTO '.table_prefix.'banlist(ban_type,ban_value,reason,is_regex) VALUES( ' . $db->escape($_POST['type']) . ', \'' . $db->escape($_POST['value']) . '\', \''.$db->escape($_POST['reason']).'\'';
- if(isset($_POST['regex'])) $q .= ', 1';
- else $q .= ', 0';
- $q .= ');';
- $e = $db->sql_query($q);
- if(!$e) $db->_die('The banlist could not be updated.');
+ $type = intval($_POST['type']);
+ $value = trim($_POST['value']);
+ if ( !in_array($type, array(BAN_IP, BAN_USER, BAN_EMAIL)) )
+ {
+ echo '<div class="error-box">Hacking attempt.</div>';
+ }
+ else if ( empty($value) )
+ {
+ echo '<div class="error-box">Please enter something to ban.</div>';
+ }
+ else
+ {
+ $entries = array();
+ $input = explode(',', $_POST['value']);
+ $error = false;
+ foreach ( $input as $entry )
+ {
+ $entry = trim($entry);
+ if ( empty($entry) )
+ {
+ echo '<div class="error-box">Malformed entry.</div>';
+ $error = true;
+ break;
+ }
+ if ( $type == BAN_IP )
+ {
+ // parse a range of addresses
+ $range = parse_ip_range($entry);
+ if ( !$range )
+ {
+ $error = true;
+ echo '<div class="error-box">Malformed IP address expression.</div>';
+ break;
+ }
+ foreach ($range as $ip)
+ {
+ $entries[] = $ip;
+ }
+ }
+ else
+ {
+ $entries[] = $entry;
+ }
+ }
+ if ( !$error )
+ {
+ $regex = ( isset($_POST['regex']) ) ? '1' : '0';
+ $to_insert = array();
+ $reason = $db->escape($_POST['reason']);
+ foreach ( $entries as $entry )
+ {
+ $entry = $db->escape($entry);
+ $to_insert[] = "($type, '$entry', '$reason', $regex)";
+ }
+ $q = 'INSERT INTO '.table_prefix."banlist(ban_type, ban_value, reason, is_regex)\n VALUES" . implode(",\n ", $to_insert) . ';';
+ @set_time_limit(0);
+ $e = $db->sql_query($q);
+ if(!$e) $db->_die('The banlist could not be updated.');
+ }
+ }
}
else if ( isset($_POST['create']) && defined('ENANO_DEMO_MODE') )
{
@@ -2077,25 +2143,29 @@
}
$q = $db->sql_query('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;');
if(!$q) $db->_die('The banlist data could not be selected.');
- echo '<table border="0" cellspacing="1" cellpadding="4">';
+ echo '<div class="tblholder" style="max-height: 800px; clip: rect(0px,auto,auto,0px); overflow: auto;">
+ <table border="0" cellspacing="1" cellpadding="4">';
echo '<tr><th>Type</th><th>Value</th><th>Regular Expression</th><th></th></tr>';
- if($db->numrows() < 1) echo '<td colspan="4">No ban rules yet.</td>';
+ if($db->numrows() < 1) echo '<td class="row1" colspan="4">No ban rules yet.</td>';
+ $cls = 'row2';
while($r = $db->fetchrow())
{
+ $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
if($r['ban_type']==BAN_IP) $t = 'IP address';
elseif($r['ban_type']==BAN_USER) $t = 'Username';
elseif($r['ban_type']==BAN_EMAIL) $t = 'E-mail address';
if($r['is_regex']) $g = 'Yes'; else $g = 'No';
- echo '<tr><td>'.$t.'</td><td>'.$r['ban_value'].'</td><td>'.$g.'</td><td><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'BanControl&action=delete&id='.$r['ban_id']).'">Delete</a></td></tr>';
+ echo '<tr><td class="'.$cls.'">'.$t.'</td><td class="'.$cls.'">'.$r['ban_value'].'</td><td class="'.$cls.'">'.$g.'</td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'BanControl&action=delete&id='.$r['ban_id']).'">Delete</a></td></tr>';
}
$db->free_result();
- echo '</table>';
+ echo '</table></div>';
echo '<h3>Create new ban rule</h3>';
echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post">';
?>
Type: <select name="type"><option value="<?php echo BAN_IP; ?>">IP address</option><option value="<?php echo BAN_USER; ?>">Username</option><option value="<?php echo BAN_EMAIL; ?>">E-mail address</option></select><br />
Rule: <input type="text" name="value" size="30" /><br />
- Reason to show to the banned user: <textarea name="reason" rows="7" cols="20"></textarea><br />
+ <small>You can ban multiple IP addresses, users, or e-mail addresses by separating entries with a single comma (User1,User2). Do not put a space after the comma. For IP addresses, you may specify ranges like 172|192.168.4-30|90-167.1-90, which will turn into 172 and 192 . 168 . 4-30 and 90-167 . 1 - 90, which matches 18,899 IP addresses. Don't specify large ranges (like the example one here) at once or you risk temporarily (~60sec) overloading the server.</small><br />
+ Reason to show to the banned user: <textarea name="reason" rows="7" cols="40"></textarea><br />
<input type="checkbox" name="regex" id="regex" /> <label for="regex">This rule is a regular expression</label> (advanced users only)<br />
<input type="submit" style="font-weight: bold;" name="create" value="Create new ban rule" />
<?php
--- a/plugins/admin/SecurityLog.php Sat Sep 08 15:06:28 2007 -0400
+++ b/plugins/admin/SecurityLog.php Sat Sep 08 22:58:38 2007 -0400
@@ -152,6 +152,10 @@
case "plugin_disable": $return .= "Disabled plugin: {$r['page_text']}"; break;
case "plugin_enable": $return .= "Enabled plugin: {$r['page_text']}"; break;
case "seclog_unauth": $return .= "Unauthorized attempt to call security log fetcher"; break;
+ case "u_from_admin": $return .= "User {$r['page_text']} demoted from Administrators group"; break;
+ case "u_from_mod": $return .= "User {$r['page_text']} demoted from Moderators group"; break;
+ case "u_to_admin": $return .= "User {$r['page_text']} added to Administrators group"; break;
+ case "u_to_mod": $return .= "User {$r['page_text']} added to Moderators group"; break;
}
$return .= '</td><td class="'.$cls.'">'.date('d M Y h:i a', $r['time_id']).'</td><td class="'.$cls.'">'.$r['author'].'</td><td class="'.$cls.'" style="cursor: pointer;" onclick="ajaxReverseDNS(this);" title="Click for reverse DNS info">'.$r['edit_summary'].'</td></tr>';
return $return;