From ea0ce16090b755f3969b36b5aaed519a297d1a3f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 15 Feb 2005 00:28:55 +0000 Subject: [PATCH] Revert last checkin by Nicolas Weeger which caused massive corruption of the file. --- includes/BlockCache.php | 140 +++++++++++++++++++++++++++++++++++++++- includes/User.php | 59 +++++++++++------ 2 files changed, 177 insertions(+), 22 deletions(-) diff --git a/includes/BlockCache.php b/includes/BlockCache.php index 6f53e61ac0..8f9a5a60aa 100644 --- a/includes/BlockCache.php +++ b/includes/BlockCache.php @@ -1 +1,139 @@ -mAddress = $address; $this->mUser = $user; $this->mBy = $by; $this->mReason = $reason; $this->mTimestamp = wfTimestamp(TS_MW,$timestamp); $this->mAuto = $auto; if( empty( $expiry ) ) { $this->mExpiry = $expiry; } else { $this->mExpiry = wfTimestamp( TS_MW, $expiry ); } $this->mForUpdate = false; $this->initialiseRange(); } /*static*/ function newFromDB( $address, $user = 0, $killExpired = true ) { $ban = new Block(); $ban->load( $address, $user, $killExpired ); return $ban; } function clear() { $mAddress = $mReason = $mTimestamp = ''; $mUser = $mBy = 0; } /** * Get a ban from the DB, with either the given address or the given username */ function load( $address = '', $user = 0, $killExpired = true ) { $fname = 'Block::load'; wfDebug( "Block::load: '$address', '$user', $killExpired\n" ); $ret = false; $killed = false; if ( $this->forUpdate() ) { $db =& wfGetDB( DB_MASTER ); $options = 'FOR UPDATE'; } else { $db =& wfGetDB( DB_SLAVE ); $options = ''; } $ipblocks = $db->tableName( 'ipblocks' ); if ( 0 == $user && $address=='' ) { $sql = "SELECT * from $ipblocks $options"; } elseif ($address=="") { $sql = "SELECT * FROM $ipblocks WHERE ipb_user={$user} $options"; } elseif ($user=="") { $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) . "' $options"; } elseif ( $options=='' ) { # If there are no optiones (e.g. FOR UPDATE), use a UNION # so that the query can make efficient use of indices $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) . "' UNION SELECT * FROM $ipblocks WHERE ipb_user={$user}"; } else { # If there are options, a UNION can not be used, use one # SELECT instead. Will do a full table scan. $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) . "' OR ipb_user={$user}) $options"; } $res = $db->query( $sql, $fname ); if ( 0 == $db->numRows( $res ) ) { # User is not blocked $this->clear(); } else { # Get first block $row = $db->fetchObject( $res ); $this->initFromRow( $row ); if ( $killExpired ) { # If requested, delete expired rows do { $killed = $this->deleteIfExpired(); if ( $killed ) { $row = $db->fetchObject( $res ); if ( $row ) { $this->initFromRow( $row ); } } } while ( $killed && $row ); # If there were any left after the killing finished, return true if ( !$row ) { $ret = false; $this->clear(); } else { $ret = true; } } else { $ret = true; } } $db->freeResult( $res ); return $ret; } function initFromRow( $row ) { $this->mAddress = $row->ipb_address; $this->mReason = $row->ipb_reason; $this->mTimestamp = wfTimestamp(TS_MW,$row->ipb_timestamp); $this->mUser = $row->ipb_user; $this->mBy = $row->ipb_by; $this->mAuto = $row->ipb_auto; $this->mId = $row->ipb_id; $this->mExpiry = $row->ipb_expiry ? wfTimestamp(TS_MW,$row->ipb_expiry) : $row->ipb_expiry; $this->initialiseRange(); } function initialiseRange() { if ( $this->mUser == 0 ) { $rangeParts = explode( '/', $this->mAddress ); if ( count( $rangeParts ) == 2 ) { $this->mNetworkBits = $rangeParts[1]; } else { $this->mNetworkBits = 32; } $this->mIntegerAddr = ip2long( $rangeParts[0] ); } else { $this->mNetworkBits = false; $this->mIntegerAddr = false; } } /** * Callback with a Block object for every block */ /*static*/ function enumBlocks( $callback, $tag, $flags = 0 ) { $block = new Block(); if ( $flags & EB_FOR_UPDATE ) { $db =& wfGetDB( DB_MASTER ); $options = 'FOR UPDATE'; $block->forUpdate( true ); } else { $db =& wfGetDB( DB_SLAVE ); $options = ''; } $ipblocks = $db->tableName( 'ipblocks' ); $sql = "SELECT * FROM $ipblocks ORDER BY ipb_timestamp DESC $options"; $res = $db->query( $sql, 'Block::enumBans' ); while ( $row = $db->fetchObject( $res ) ) { $block->initFromRow( $row ); if ( !( $flags & EB_KEEP_EXPIRED ) ) { if ( !$block->deleteIfExpired() ) { $callback( $block, $tag ); } } else { $callback( $block, $tag ); } } wfFreeResult( $res ); } function delete() { $fname = 'Block::delete'; $dbw =& wfGetDB( DB_MASTER ); if ( $this->mAddress == '' ) { $condition = array( 'ipb_id' => $this->mId ); } else { $condition = array( 'ipb_address' => $this->mAddress ); } $dbw->delete( 'ipblocks', $condition, $fname ); $this->clearCache(); } function insert() { wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" ); $dbw =& wfGetDB( DB_MASTER ); $dbw->insert( 'ipblocks', array( 'ipb_address' => $this->mAddress, 'ipb_user' => $this->mUser, 'ipb_by' => $this->mBy, 'ipb_reason' => $this->mReason, 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp), 'ipb_auto' => $this->mAuto, 'ipb_expiry' => $this->mExpiry ? $dbw->timestamp($this->mExpiry) : $this->mExpiry, ), 'Block::insert' ); $this->clearCache(); } function deleteIfExpired() { if ( $this->isExpired() ) { wfDebug( "Block::deleteIfExpired() -- deleting\n" ); $this->delete(); return true; } else { wfDebug( "Block::deleteIfExpired() -- not expired\n" ); return false; } } function isExpired() { wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" ); if ( !$this->mExpiry ) { return false; } else { return wfTimestampNow() > $this->mExpiry; } } function isValid() { return $this->mAddress != ''; } function updateTimestamp() { if ( $this->mAuto ) { $this->mTimestamp = wfTimestamp(); $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp ); $dbw =& wfGetDB( DB_MASTER ); $dbw->update( 'ipblocks', array( /* SET */ 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp), 'ipb_expiry' => $dbw->timestamp($this->mExpiry), ), array( /* WHERE */ 'ipb_address' => $this->mAddress ), 'Block::updateTimestamp' ); $this->clearCache(); } } /* private */ function clearCache() { global $wgBlockCache; if ( is_object( $wgBlockCache ) ) { $wgBlockCache->loadFromDB(); } } function getIntegerAddr() { return $this->mIntegerAddr; } function getNetworkBits() { return $this->mNetworkBits; } function forUpdate( $x = NULL ) { return wfSetVar( $this->mForUpdate, $x ); } /* static */ function getAutoblockExpiry( $timestamp ) { global $wgAutoblockExpiry; return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry ); } /* static */ function normaliseRange( $range ) { $parts = explode( '/', $range ); if ( count( $parts ) == 2 ) { $shift = 32 - $parts[1]; $ipint = ip2long( $parts[0] ); $ipint = $ipint >> $shift << $shift; $newip = long2ip( $ipint ); $range = "$newip/{$parts[1]}"; } return $range; }}?> \ No newline at end of file +mMemcKey = $dbName.':ipblocks'; + + if ( !$deferLoad ) { + $this->load(); + } + } + + /** + * Load the blocks from the database and save them to memcached + */ + function loadFromDB() { + global $wgUseMemCached, $wgMemc; + $this->mData = array(); + # Selecting FOR UPDATE is a convenient way to serialise the memcached and DB operations, + # which is necessary even though we don't update the DB + if ( $wgUseMemCached ) { + Block::enumBlocks( 'wfBlockCacheInsert', '', EB_FOR_UPDATE ); + $wgMemc->set( $this->mMemcKey, $this->mData, 0 ); + } else { + Block::enumBlocks( 'wfBlockCacheInsert', '' ); + } + } + + /** + * Load the cache from memcached or, if that's not possible, from the DB + */ + function load() { + global $wgUseMemCached, $wgMemc; + + if ( $this->mData === false) { + # Try memcached + if ( $wgUseMemCached ) { + $this->mData = $wgMemc->get( $this->mMemcKey ); + } + + if ( !is_array( $this->mData ) ) { + $this->loadFromDB(); + } + } + } + + /** + * Add a block to the cache + * + * @param Object &$block Reference to a "Block" object. + */ + function insert( &$block ) { + if ( $block->mUser == 0 ) { + $nb = $block->getNetworkBits(); + $ipint = $block->getIntegerAddr(); + $index = $ipint >> ( 32 - $nb ); + + if ( !array_key_exists( $nb, $this->mData ) ) { + $this->mData[$nb] = array(); + } + + $this->mData[$nb][$index] = 1; + } + } + + /** + * Find out if a given IP address is blocked + * + * @param String $ip IP address + */ + function get( $ip ) { + $this->load(); + $ipint = ip2long( $ip ); + $blocked = false; + + foreach ( $this->mData as $networkBits => $blockInts ) { + if ( array_key_exists( $ipint >> ( 32 - $networkBits ), $blockInts ) ) { + $blocked = true; + break; + } + } + if ( $blocked ) { + # Clear low order bits + if ( $networkBits != 32 ) { + $ip .= '/'.$networkBits; + $ip = Block::normaliseRange( $ip ); + } + $block = new Block(); + $block->load( $ip ); + } else { + $block = false; + } + + return $block; + } + + /** + * Clear the local cache + * There was once a clear() to clear memcached too, but I deleted it + */ + function clearLocal() { + $this->mData = false; + } +} + +/** + * Add a block to the global $wgBlockCache + * + * @param Object $block A "Block"-object + * @param Any $tag unused + */ +function wfBlockCacheInsert( $block, $tag ) { + global $wgBlockCache; + $wgBlockCache->insert( $block ); +} diff --git a/includes/User.php b/includes/User.php index 5272730f53..a116389938 100644 --- a/includes/User.php +++ b/includes/User.php @@ -242,16 +242,8 @@ class User { /** * Get blocking information * @access private - * @param bool $bFromSlave Specify whether to check slave or master. To improve performance, - * non-critical checks are done against slaves. Check when actually saving should be done against - * master. - * - * Note that even if $bFromSlave is false, the check is done first against slave, then master. - * The logic is that if blocked on slave, we'll assume it's either blocked on master or - * just slightly outta sync and soon corrected - safer to block slightly more that less. - * And it's cheaper to check slave first, then master if needed, than master always. */ - function getBlockedStatus( $bFromSlave = false ) { + function getBlockedStatus() { global $wgIP, $wgBlockCache, $wgProxyList; if ( -1 != $this->mBlockedby ) { return; } @@ -261,8 +253,7 @@ class User { # User blocking if ( $this->mId ) { $block = new Block(); - $block->forUpdate( $bFromSlave ); - if ( $block->load( $wgIP , $this->mId ) ) { + if ( $block->load( $wgIP , $this->mId ) ) { $this->mBlockedby = $block->mBy; $this->mBlockreason = $block->mReason; } @@ -270,14 +261,7 @@ class User { # IP/range blocking if ( !$this->mBlockedby ) { - # Check first against slave, and optionally from master. - $block = $wgBlockCache->get( $wgIP, true ); - if ( !block && !$bFromSlave ) - { - # Not blocked: check against master, to make sure. - $wgBlockCache->clearLocal( ); - $block = $wgBlockCache->get( $wgIP, false ); - } + $block = $wgBlockCache->get( $wgIP ); if ( $block !== false ) { $this->mBlockedby = $block->mBy; $this->mBlockreason = $block->mReason; @@ -297,8 +281,8 @@ class User { * Check if user is blocked * @return bool True if blocked, false otherwise */ - function isBlocked( $bFromSlave = false ) { - $this->getBlockedStatus( $bFromSlave ); + function isBlocked() { + $this->getBlockedStatus(); if ( 0 === $this->mBlockedby ) { return false; } return true; } @@ -1203,6 +1187,39 @@ class User { } return false; } + + /** + * Initialize (if necessary) and return a session token value + * which can be used in edit forms to show that the user's + * login credentials aren't being hijacked with a foreign form + * submission. + * + * @return string + * @access public + */ + function editToken() { + if( !isset( $_SESSION['wsEditToken'] ) ) { + $token = dechex( mt_rand() ) . dechex( mt_rand() ); + $_SESSION['wsEditToken'] = $token; + } + return $_SESSION['wsEditToken']; + } + + /** + * Check given value against the token value stored in the session. + * A match should confirm that the form was submitted from the + * user's own login session, not a form submission from a third-party + * site. + * + * @param string $val + * @return bool + * @access public + */ + function matchEditToken( $val ) { + if( !isset( $_SESSION['wsEditToken'] ) ) + return false; + return $_SESSION['wsEditToken'] == $val; + } } ?> -- 2.20.1