MediaWiki  REL1_21
Block.php
Go to the documentation of this file.
00001 <?php
00022 class Block {
00023         /* public*/ var $mReason, $mTimestamp, $mAuto, $mExpiry, $mHideName;
00024 
00025         protected
00026                 $mId,
00027                 $mFromMaster,
00028 
00029                 $mBlockEmail,
00030                 $mDisableUsertalk,
00031                 $mCreateAccount,
00032                 $mParentBlockId;
00033 
00035         protected $target;
00036 
00037         // @var Integer Hack for foreign blocking (CentralAuth)
00038         protected $forcedTargetID;
00039 
00041         protected $type;
00042 
00044         protected $blocker;
00045 
00047         protected $isHardblock = true;
00048 
00050         protected $isAutoblocking = true;
00051 
00052         # TYPE constants
00053         const TYPE_USER = 1;
00054         const TYPE_IP = 2;
00055         const TYPE_RANGE = 3;
00056         const TYPE_AUTO = 4;
00057         const TYPE_ID = 5;
00058 
00064         function __construct( $address = '', $user = 0, $by = 0, $reason = '',
00065                 $timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
00066                 $hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byText = '' )
00067         {
00068                 if( $timestamp === 0 ) {
00069                         $timestamp = wfTimestampNow();
00070                 }
00071 
00072                 if( count( func_get_args() ) > 0 ) {
00073                         # Soon... :D
00074                         # wfDeprecated( __METHOD__ . " with arguments" );
00075                 }
00076 
00077                 $this->setTarget( $address );
00078                 if ( $this->target instanceof User && $user ) {
00079                         $this->forcedTargetID = $user; // needed for foreign users
00080                 }
00081                 if ( $by ) { // local user
00082                         $this->setBlocker( User::newFromID( $by ) );
00083                 } else { // foreign user
00084                         $this->setBlocker( $byText );
00085                 }
00086                 $this->mReason = $reason;
00087                 $this->mTimestamp = wfTimestamp( TS_MW, $timestamp );
00088                 $this->mAuto = $auto;
00089                 $this->isHardblock( !$anonOnly );
00090                 $this->prevents( 'createaccount', $createAccount );
00091                 if ( $expiry == 'infinity' || $expiry == wfGetDB( DB_SLAVE )->getInfinity() ) {
00092                         $this->mExpiry = 'infinity';
00093                 } else {
00094                         $this->mExpiry = wfTimestamp( TS_MW, $expiry );
00095                 }
00096                 $this->isAutoblocking( $enableAutoblock );
00097                 $this->mHideName = $hideName;
00098                 $this->prevents( 'sendemail', $blockEmail );
00099                 $this->prevents( 'editownusertalk', !$allowUsertalk );
00100 
00101                 $this->mFromMaster = false;
00102         }
00103 
00114         public static function newFromDB( $address, $user = 0 ) {
00115                 wfDeprecated( __METHOD__, '1.18' );
00116                 return self::newFromTarget( User::whoIs( $user ), $address );
00117         }
00118 
00125         public static function newFromID( $id ) {
00126                 $dbr = wfGetDB( DB_SLAVE );
00127                 $res = $dbr->selectRow(
00128                         'ipblocks',
00129                         self::selectFields(),
00130                         array( 'ipb_id' => $id ),
00131                         __METHOD__
00132                 );
00133                 if ( $res ) {
00134                         return self::newFromRow( $res );
00135                 } else {
00136                         return null;
00137                 }
00138         }
00139 
00145         public static function selectFields() {
00146                 return array(
00147                         'ipb_id',
00148                         'ipb_address',
00149                         'ipb_by',
00150                         'ipb_by_text',
00151                         'ipb_reason',
00152                         'ipb_timestamp',
00153                         'ipb_auto',
00154                         'ipb_anon_only',
00155                         'ipb_create_account',
00156                         'ipb_enable_autoblock',
00157                         'ipb_expiry',
00158                         'ipb_deleted',
00159                         'ipb_block_email',
00160                         'ipb_allow_usertalk',
00161                         'ipb_parent_block_id',
00162                 );
00163         }
00164 
00173         public function equals( Block $block ) {
00174                 return (
00175                         (string)$this->target == (string)$block->target
00176                         && $this->type == $block->type
00177                         && $this->mAuto == $block->mAuto
00178                         && $this->isHardblock() == $block->isHardblock()
00179                         && $this->prevents( 'createaccount' ) == $block->prevents( 'createaccount' )
00180                         && $this->mExpiry == $block->mExpiry
00181                         && $this->isAutoblocking() == $block->isAutoblocking()
00182                         && $this->mHideName == $block->mHideName
00183                         && $this->prevents( 'sendemail' ) == $block->prevents( 'sendemail' )
00184                         && $this->prevents( 'editownusertalk' ) == $block->prevents( 'editownusertalk' )
00185                         && $this->mReason == $block->mReason
00186                 );
00187         }
00188 
00194         public function clear() {
00195                 wfDeprecated( __METHOD__, '1.18' );
00196                 # Noop
00197         }
00198 
00207         public function load( $address = '', $user = 0 ) {
00208                 wfDeprecated( __METHOD__, '1.18' );
00209                 if( $user ) {
00210                         $username = User::whoIs( $user );
00211                         $block = self::newFromTarget( $username, $address );
00212                 } else {
00213                         $block = self::newFromTarget( null, $address );
00214                 }
00215 
00216                 if( $block instanceof Block ) {
00217                         # This is mildly evil, but hey, it's B/C :D
00218                         foreach( $block as $variable => $value ) {
00219                                 $this->$variable = $value;
00220                         }
00221                         return true;
00222                 } else {
00223                         return false;
00224                 }
00225         }
00226 
00237         protected function newLoad( $vagueTarget = null ) {
00238                 $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
00239 
00240                 if( $this->type !== null ) {
00241                         $conds = array(
00242                                 'ipb_address' => array( (string)$this->target ),
00243                         );
00244                 } else {
00245                         $conds = array( 'ipb_address' => array() );
00246                 }
00247 
00248                 # Be aware that the != '' check is explicit, since empty values will be
00249                 # passed by some callers (bug 29116)
00250                 if( $vagueTarget != '' ) {
00251                         list( $target, $type ) = self::parseTarget( $vagueTarget );
00252                         switch( $type ) {
00253                                 case self::TYPE_USER:
00254                                         # Slightly weird, but who are we to argue?
00255                                         $conds['ipb_address'][] = (string)$target;
00256                                         break;
00257 
00258                                 case self::TYPE_IP:
00259                                         $conds['ipb_address'][] = (string)$target;
00260                                         $conds[] = self::getRangeCond( IP::toHex( $target ) );
00261                                         $conds = $db->makeList( $conds, LIST_OR );
00262                                         break;
00263 
00264                                 case self::TYPE_RANGE:
00265                                         list( $start, $end ) = IP::parseRange( $target );
00266                                         $conds['ipb_address'][] = (string)$target;
00267                                         $conds[] = self::getRangeCond( $start, $end );
00268                                         $conds = $db->makeList( $conds, LIST_OR );
00269                                         break;
00270 
00271                                 default:
00272                                         throw new MWException( "Tried to load block with invalid type" );
00273                         }
00274                 }
00275 
00276                 $res = $db->select( 'ipblocks', self::selectFields(), $conds, __METHOD__ );
00277 
00278                 # This result could contain a block on the user, a block on the IP, and a russian-doll
00279                 # set of rangeblocks.  We want to choose the most specific one, so keep a leader board.
00280                 $bestRow = null;
00281 
00282                 # Lower will be better
00283                 $bestBlockScore = 100;
00284 
00285                 # This is begging for $this = $bestBlock, but that's not allowed in PHP :(
00286                 $bestBlockPreventsEdit = null;
00287 
00288                 foreach( $res as $row ) {
00289                         $block = self::newFromRow( $row );
00290 
00291                         # Don't use expired blocks
00292                         if( $block->deleteIfExpired() ) {
00293                                 continue;
00294                         }
00295 
00296                         # Don't use anon only blocks on users
00297                         if( $this->type == self::TYPE_USER && !$block->isHardblock() ) {
00298                                 continue;
00299                         }
00300 
00301                         if( $block->getType() == self::TYPE_RANGE ) {
00302                                 # This is the number of bits that are allowed to vary in the block, give
00303                                 # or take some floating point errors
00304                                 $end = wfBaseconvert( $block->getRangeEnd(), 16, 10 );
00305                                 $start = wfBaseconvert( $block->getRangeStart(), 16, 10 );
00306                                 $size = log( $end - $start + 1, 2 );
00307 
00308                                 # This has the nice property that a /32 block is ranked equally with a
00309                                 # single-IP block, which is exactly what it is...
00310                                 $score = self::TYPE_RANGE - 1 + ( $size / 128 );
00311 
00312                         } else {
00313                                 $score = $block->getType();
00314                         }
00315 
00316                         if( $score < $bestBlockScore ) {
00317                                 $bestBlockScore = $score;
00318                                 $bestRow = $row;
00319                                 $bestBlockPreventsEdit = $block->prevents( 'edit' );
00320                         }
00321                 }
00322 
00323                 if( $bestRow !== null ) {
00324                         $this->initFromRow( $bestRow );
00325                         $this->prevents( 'edit', $bestBlockPreventsEdit );
00326                         return true;
00327                 } else {
00328                         return false;
00329                 }
00330         }
00331 
00338         public static function getRangeCond( $start, $end = null ) {
00339                 if ( $end === null ) {
00340                         $end = $start;
00341                 }
00342                 # Per bug 14634, we want to include relevant active rangeblocks; for
00343                 # rangeblocks, we want to include larger ranges which enclose the given
00344                 # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
00345                 # so we can improve performance by filtering on a LIKE clause
00346                 $chunk = self::getIpFragment( $start );
00347                 $dbr = wfGetDB( DB_SLAVE );
00348                 $like = $dbr->buildLike( $chunk, $dbr->anyString() );
00349 
00350                 # Fairly hard to make a malicious SQL statement out of hex characters,
00351                 # but stranger things have happened...
00352                 $safeStart = $dbr->addQuotes( $start );
00353                 $safeEnd = $dbr->addQuotes( $end );
00354 
00355                 return $dbr->makeList(
00356                         array(
00357                                 "ipb_range_start $like",
00358                                 "ipb_range_start <= $safeStart",
00359                                 "ipb_range_end >= $safeEnd",
00360                         ),
00361                         LIST_AND
00362                 );
00363         }
00364 
00371         protected static function getIpFragment( $hex ) {
00372                 global $wgBlockCIDRLimit;
00373                 if ( substr( $hex, 0, 3 ) == 'v6-' ) {
00374                         return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
00375                 } else {
00376                         return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
00377                 }
00378         }
00379 
00385         protected function initFromRow( $row ) {
00386                 $this->setTarget( $row->ipb_address );
00387                 if ( $row->ipb_by ) { // local user
00388                         $this->setBlocker( User::newFromID( $row->ipb_by ) );
00389                 } else { // foreign user
00390                         $this->setBlocker( $row->ipb_by_text );
00391                 }
00392 
00393                 $this->mReason = $row->ipb_reason;
00394                 $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
00395                 $this->mAuto = $row->ipb_auto;
00396                 $this->mHideName = $row->ipb_deleted;
00397                 $this->mId = $row->ipb_id;
00398                 $this->mParentBlockId = $row->ipb_parent_block_id;
00399 
00400                 // I wish I didn't have to do this
00401                 $db = wfGetDB( DB_SLAVE );
00402                 if ( $row->ipb_expiry == $db->getInfinity() ) {
00403                         $this->mExpiry = 'infinity';
00404                 } else {
00405                         $this->mExpiry = wfTimestamp( TS_MW, $row->ipb_expiry );
00406                 }
00407 
00408                 $this->isHardblock( !$row->ipb_anon_only );
00409                 $this->isAutoblocking( $row->ipb_enable_autoblock );
00410 
00411                 $this->prevents( 'createaccount', $row->ipb_create_account );
00412                 $this->prevents( 'sendemail', $row->ipb_block_email );
00413                 $this->prevents( 'editownusertalk', !$row->ipb_allow_usertalk );
00414         }
00415 
00421         public static function newFromRow( $row ) {
00422                 $block = new Block;
00423                 $block->initFromRow( $row );
00424                 return $block;
00425         }
00426 
00433         public function delete() {
00434                 if ( wfReadOnly() ) {
00435                         return false;
00436                 }
00437 
00438                 if ( !$this->getId() ) {
00439                         throw new MWException( "Block::delete() requires that the mId member be filled\n" );
00440                 }
00441 
00442                 $dbw = wfGetDB( DB_MASTER );
00443                 $dbw->delete( 'ipblocks', array( 'ipb_parent_block_id' => $this->getId() ), __METHOD__ );
00444                 $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->getId() ), __METHOD__ );
00445 
00446                 return $dbw->affectedRows() > 0;
00447         }
00448 
00457         public function insert( $dbw = null ) {
00458                 wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
00459 
00460                 if ( $dbw === null ) {
00461                         $dbw = wfGetDB( DB_MASTER );
00462                 }
00463 
00464                 # Don't collide with expired blocks
00465                 Block::purgeExpired();
00466 
00467                 $row = $this->getDatabaseArray();
00468                 $row['ipb_id'] = $dbw->nextSequenceValue( "ipblocks_ipb_id_seq" );
00469 
00470                 $dbw->insert(
00471                         'ipblocks',
00472                         $row,
00473                         __METHOD__,
00474                         array( 'IGNORE' )
00475                 );
00476                 $affected = $dbw->affectedRows();
00477                 $this->mId = $dbw->insertId();
00478 
00479                 if ( $affected ) {
00480                         $auto_ipd_ids = $this->doRetroactiveAutoblock();
00481                         return array( 'id' => $this->mId, 'autoIds' => $auto_ipd_ids );
00482                 }
00483 
00484                 return false;
00485         }
00486 
00494         public function update() {
00495                 wfDebug( "Block::update; timestamp {$this->mTimestamp}\n" );
00496                 $dbw = wfGetDB( DB_MASTER );
00497 
00498                 $dbw->update(
00499                         'ipblocks',
00500                         $this->getDatabaseArray( $dbw ),
00501                         array( 'ipb_id' => $this->getId() ),
00502                         __METHOD__
00503                 );
00504 
00505                 return $dbw->affectedRows();
00506         }
00507 
00513         protected function getDatabaseArray( $db = null ) {
00514                 if( !$db ) {
00515                         $db = wfGetDB( DB_SLAVE );
00516                 }
00517                 $expiry = $db->encodeExpiry( $this->mExpiry );
00518 
00519                 if ( $this->forcedTargetID ) {
00520                         $uid = $this->forcedTargetID;
00521                 } else {
00522                         $uid = $this->target instanceof User ? $this->target->getID() : 0;
00523                 }
00524 
00525                 $a = array(
00526                         'ipb_address'          => (string)$this->target,
00527                         'ipb_user'             => $uid,
00528                         'ipb_by'               => $this->getBy(),
00529                         'ipb_by_text'          => $this->getByName(),
00530                         'ipb_reason'           => $this->mReason,
00531                         'ipb_timestamp'        => $db->timestamp( $this->mTimestamp ),
00532                         'ipb_auto'             => $this->mAuto,
00533                         'ipb_anon_only'        => !$this->isHardblock(),
00534                         'ipb_create_account'   => $this->prevents( 'createaccount' ),
00535                         'ipb_enable_autoblock' => $this->isAutoblocking(),
00536                         'ipb_expiry'           => $expiry,
00537                         'ipb_range_start'      => $this->getRangeStart(),
00538                         'ipb_range_end'        => $this->getRangeEnd(),
00539                         'ipb_deleted'          => intval( $this->mHideName ), // typecast required for SQLite
00540                         'ipb_block_email'      => $this->prevents( 'sendemail' ),
00541                         'ipb_allow_usertalk'   => !$this->prevents( 'editownusertalk' ),
00542                         'ipb_parent_block_id'  => $this->mParentBlockId
00543                 );
00544 
00545                 return $a;
00546         }
00547 
00554         protected function doRetroactiveAutoblock() {
00555                 $blockIds = array();
00556                 # If autoblock is enabled, autoblock the LAST IP(s) used
00557                 if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
00558                         wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
00559 
00560                         $continue = wfRunHooks(
00561                                 'PerformRetroactiveAutoblock', array( $this, &$blockIds ) );
00562 
00563                         if ( $continue ) {
00564                                 self::defaultRetroactiveAutoblock( $this, $blockIds );
00565                         }
00566                 }
00567                 return $blockIds;
00568         }
00569 
00578         protected static function defaultRetroactiveAutoblock( Block $block, array &$blockIds ) {
00579                 global $wgPutIPinRC;
00580 
00581                 // No IPs are in recentchanges table, so nothing to select
00582                 if( !$wgPutIPinRC ) {
00583                         return;
00584                 }
00585 
00586                 $dbr = wfGetDB( DB_SLAVE );
00587 
00588                 $options = array( 'ORDER BY' => 'rc_timestamp DESC' );
00589                 $conds = array( 'rc_user_text' => (string)$block->getTarget() );
00590 
00591                 // Just the last IP used.
00592                 $options['LIMIT'] = 1;
00593 
00594                 $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
00595                         __METHOD__, $options );
00596 
00597                 if ( !$res->numRows() ) {
00598                         # No results, don't autoblock anything
00599                         wfDebug( "No IP found to retroactively autoblock\n" );
00600                 } else {
00601                         foreach ( $res as $row ) {
00602                                 if ( $row->rc_ip ) {
00603                                         $id = $block->doAutoblock( $row->rc_ip );
00604                                         if ( $id ) $blockIds[] = $id;
00605                                 }
00606                         }
00607                 }
00608         }
00609 
00617         public static function isWhitelistedFromAutoblocks( $ip ) {
00618                 global $wgMemc;
00619 
00620                 // Try to get the autoblock_whitelist from the cache, as it's faster
00621                 // than getting the msg raw and explode()'ing it.
00622                 $key = wfMemcKey( 'ipb', 'autoblock', 'whitelist' );
00623                 $lines = $wgMemc->get( $key );
00624                 if ( !$lines ) {
00625                         $lines = explode( "\n", wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() );
00626                         $wgMemc->set( $key, $lines, 3600 * 24 );
00627                 }
00628 
00629                 wfDebug( "Checking the autoblock whitelist..\n" );
00630 
00631                 foreach ( $lines as $line ) {
00632                         # List items only
00633                         if ( substr( $line, 0, 1 ) !== '*' ) {
00634                                 continue;
00635                         }
00636 
00637                         $wlEntry = substr( $line, 1 );
00638                         $wlEntry = trim( $wlEntry );
00639 
00640                         wfDebug( "Checking $ip against $wlEntry..." );
00641 
00642                         # Is the IP in this range?
00643                         if ( IP::isInRange( $ip, $wlEntry ) ) {
00644                                 wfDebug( " IP $ip matches $wlEntry, not autoblocking\n" );
00645                                 return true;
00646                         } else {
00647                                 wfDebug( " No match\n" );
00648                         }
00649                 }
00650 
00651                 return false;
00652         }
00653 
00660         public function doAutoblock( $autoblockIP ) {
00661                 # If autoblocks are disabled, go away.
00662                 if ( !$this->isAutoblocking() ) {
00663                         return false;
00664                 }
00665 
00666                 # Check for presence on the autoblock whitelist.
00667                 if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
00668                         return false;
00669                 }
00670 
00671                 # Allow hooks to cancel the autoblock.
00672                 if ( !wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
00673                         wfDebug( "Autoblock aborted by hook.\n" );
00674                         return false;
00675                 }
00676 
00677                 # It's okay to autoblock. Go ahead and insert/update the block...
00678 
00679                 # Do not add a *new* block if the IP is already blocked.
00680                 $ipblock = Block::newFromTarget( $autoblockIP );
00681                 if ( $ipblock ) {
00682                         # Check if the block is an autoblock and would exceed the user block
00683                         # if renewed. If so, do nothing, otherwise prolong the block time...
00684                         if ( $ipblock->mAuto && // @TODO: why not compare $ipblock->mExpiry?
00685                                 $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
00686                         ) {
00687                                 # Reset block timestamp to now and its expiry to
00688                                 # $wgAutoblockExpiry in the future
00689                                 $ipblock->updateTimestamp();
00690                         }
00691                         return false;
00692                 }
00693 
00694                 # Make a new block object with the desired properties.
00695                 $autoblock = new Block;
00696                 wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
00697                 $autoblock->setTarget( $autoblockIP );
00698                 $autoblock->setBlocker( $this->getBlocker() );
00699                 $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )->inContentLanguage()->plain();
00700                 $timestamp = wfTimestampNow();
00701                 $autoblock->mTimestamp = $timestamp;
00702                 $autoblock->mAuto = 1;
00703                 $autoblock->prevents( 'createaccount', $this->prevents( 'createaccount' ) );
00704                 # Continue suppressing the name if needed
00705                 $autoblock->mHideName = $this->mHideName;
00706                 $autoblock->prevents( 'editownusertalk', $this->prevents( 'editownusertalk' ) );
00707                 $autoblock->mParentBlockId = $this->mId;
00708 
00709                 if ( $this->mExpiry == 'infinity' ) {
00710                         # Original block was indefinite, start an autoblock now
00711                         $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
00712                 } else {
00713                         # If the user is already blocked with an expiry date, we don't
00714                         # want to pile on top of that.
00715                         $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
00716                 }
00717 
00718                 # Insert the block...
00719                 $status = $autoblock->insert();
00720                 return $status
00721                         ? $status['id']
00722                         : false;
00723         }
00724 
00729         public function deleteIfExpired() {
00730                 wfProfileIn( __METHOD__ );
00731 
00732                 if ( $this->isExpired() ) {
00733                         wfDebug( "Block::deleteIfExpired() -- deleting\n" );
00734                         $this->delete();
00735                         $retVal = true;
00736                 } else {
00737                         wfDebug( "Block::deleteIfExpired() -- not expired\n" );
00738                         $retVal = false;
00739                 }
00740 
00741                 wfProfileOut( __METHOD__ );
00742                 return $retVal;
00743         }
00744 
00749         public function isExpired() {
00750                 $timestamp = wfTimestampNow();
00751                 wfDebug( "Block::isExpired() checking current " . $timestamp . " vs $this->mExpiry\n" );
00752 
00753                 if ( !$this->mExpiry ) {
00754                         return false;
00755                 } else {
00756                         return $timestamp > $this->mExpiry;
00757                 }
00758         }
00759 
00764         public function isValid() {
00765                 return $this->getTarget() != null;
00766         }
00767 
00771         public function updateTimestamp() {
00772                 if ( $this->mAuto ) {
00773                         $this->mTimestamp = wfTimestamp();
00774                         $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
00775 
00776                         $dbw = wfGetDB( DB_MASTER );
00777                         $dbw->update( 'ipblocks',
00778                                 array( /* SET */
00779                                         'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
00780                                         'ipb_expiry' => $dbw->timestamp( $this->mExpiry ),
00781                                 ),
00782                                 array( /* WHERE */
00783                                         'ipb_address' => (string)$this->getTarget()
00784                                 ),
00785                                 __METHOD__
00786                         );
00787                 }
00788         }
00789 
00795         public function getRangeStart() {
00796                 switch( $this->type ) {
00797                         case self::TYPE_USER:
00798                                 return '';
00799                         case self::TYPE_IP:
00800                                 return IP::toHex( $this->target );
00801                         case self::TYPE_RANGE:
00802                                 list( $start, /*...*/ ) = IP::parseRange( $this->target );
00803                                 return $start;
00804                         default: throw new MWException( "Block with invalid type" );
00805                 }
00806         }
00807 
00813         public function getRangeEnd() {
00814                 switch( $this->type ) {
00815                         case self::TYPE_USER:
00816                                 return '';
00817                         case self::TYPE_IP:
00818                                 return IP::toHex( $this->target );
00819                         case self::TYPE_RANGE:
00820                                 list( /*...*/, $end ) = IP::parseRange( $this->target );
00821                                 return $end;
00822                         default: throw new MWException( "Block with invalid type" );
00823                 }
00824         }
00825 
00831         public function getBy() {
00832                 $blocker = $this->getBlocker();
00833                 return ( $blocker instanceof User )
00834                         ? $blocker->getId()
00835                         : 0;
00836         }
00837 
00843         public function getByName() {
00844                 $blocker = $this->getBlocker();
00845                 return ( $blocker instanceof User )
00846                         ? $blocker->getName()
00847                         : (string)$blocker; // username
00848         }
00849 
00854         public function getId() {
00855                 return $this->mId;
00856         }
00857 
00864         public function forUpdate( $x = null ) {
00865                 wfDeprecated( __METHOD__, '1.18' );
00866                 # noop
00867         }
00868 
00875         public function fromMaster( $x = null ) {
00876                 return wfSetVar( $this->mFromMaster, $x );
00877         }
00878 
00884         public function isHardblock( $x = null ) {
00885                 wfSetVar( $this->isHardblock, $x );
00886 
00887                 # You can't *not* hardblock a user
00888                 return $this->getType() == self::TYPE_USER
00889                         ? true
00890                         : $this->isHardblock;
00891         }
00892 
00893         public function isAutoblocking( $x = null ) {
00894                 wfSetVar( $this->isAutoblocking, $x );
00895 
00896                 # You can't put an autoblock on an IP or range as we don't have any history to
00897                 # look over to get more IPs from
00898                 return $this->getType() == self::TYPE_USER
00899                         ? $this->isAutoblocking
00900                         : false;
00901         }
00902 
00909         public function prevents( $action, $x = null ) {
00910                 switch( $action ) {
00911                         case 'edit':
00912                                 # For now... <evil laugh>
00913                                 return true;
00914 
00915                         case 'createaccount':
00916                                 return wfSetVar( $this->mCreateAccount, $x );
00917 
00918                         case 'sendemail':
00919                                 return wfSetVar( $this->mBlockEmail, $x );
00920 
00921                         case 'editownusertalk':
00922                                 return wfSetVar( $this->mDisableUsertalk, $x );
00923 
00924                         default:
00925                                 return null;
00926                 }
00927         }
00928 
00933         public function getRedactedName() {
00934                 if ( $this->mAuto ) {
00935                         return Html::rawElement(
00936                                 'span',
00937                                 array( 'class' => 'mw-autoblockid' ),
00938                                 wfMessage( 'autoblockid', $this->mId )
00939                         );
00940                 } else {
00941                         return htmlspecialchars( $this->getTarget() );
00942                 }
00943         }
00944 
00953         public static function encodeExpiry( $expiry, $db ) {
00954                 wfDeprecated( __METHOD__, '1.18' );
00955                 return $db->encodeExpiry( $expiry );
00956         }
00957 
00966         public static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
00967                 wfDeprecated( __METHOD__, '1.18' );
00968                 global $wgContLang;
00969                 return $wgContLang->formatExpiry( $expiry, $timestampType );
00970         }
00971 
00978         public static function getAutoblockExpiry( $timestamp ) {
00979                 global $wgAutoblockExpiry;
00980 
00981                 return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
00982         }
00983 
00991         public static function normaliseRange( $range ) {
00992                 wfDeprecated( __METHOD__, '1.18' );
00993                 return IP::sanitizeRange( $range );
00994         }
00995 
00999         public static function purgeExpired() {
01000                 if ( !wfReadOnly() ) {
01001                         $dbw = wfGetDB( DB_MASTER );
01002                         $dbw->delete( 'ipblocks',
01003                                 array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
01004                 }
01005         }
01006 
01013         public static function infinity() {
01014                 wfDeprecated( __METHOD__, '1.18' );
01015                 return wfGetDB( DB_SLAVE )->getInfinity();
01016         }
01017 
01025         public static function parseExpiryInput( $expiry ) {
01026                 wfDeprecated( __METHOD__, '1.18' );
01027                 return SpecialBlock::parseExpiryInput( $expiry );
01028         }
01029 
01050         public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
01051 
01052                 list( $target, $type ) = self::parseTarget( $specificTarget );
01053                 if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
01054                         return Block::newFromID( $target );
01055 
01056                 } elseif( $target === null && $vagueTarget == '' ) {
01057                         # We're not going to find anything useful here
01058                         # Be aware that the == '' check is explicit, since empty values will be
01059                         # passed by some callers (bug 29116)
01060                         return null;
01061 
01062                 } elseif( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ) ) ) {
01063                         $block = new Block();
01064                         $block->fromMaster( $fromMaster );
01065 
01066                         if( $type !== null ) {
01067                                 $block->setTarget( $target );
01068                         }
01069 
01070                         if( $block->newLoad( $vagueTarget ) ) {
01071                                 return $block;
01072                         }
01073                 }
01074                 return null;
01075         }
01076 
01086         public static function parseTarget( $target ) {
01087                 # We may have been through this before
01088                 if( $target instanceof User ) {
01089                         if( IP::isValid( $target->getName() ) ) {
01090                                 return array( $target, self::TYPE_IP );
01091                         } else {
01092                                 return array( $target, self::TYPE_USER );
01093                         }
01094                 } elseif( $target === null ) {
01095                         return array( null, null );
01096                 }
01097 
01098                 $target = trim( $target );
01099 
01100                 if ( IP::isValid( $target ) ) {
01101                         # We can still create a User if it's an IP address, but we need to turn
01102                         # off validation checking (which would exclude IP addresses)
01103                         return array(
01104                                 User::newFromName( IP::sanitizeIP( $target ), false ),
01105                                 Block::TYPE_IP
01106                         );
01107 
01108                 } elseif ( IP::isValidBlock( $target ) ) {
01109                         # Can't create a User from an IP range
01110                         return array( IP::sanitizeRange( $target ), Block::TYPE_RANGE );
01111                 }
01112 
01113                 # Consider the possibility that this is not a username at all
01114                 # but actually an old subpage (bug #29797)
01115                 if( strpos( $target, '/' ) !== false ) {
01116                         # An old subpage, drill down to the user behind it
01117                         $parts = explode( '/', $target );
01118                         $target = $parts[0];
01119                 }
01120 
01121                 $userObj = User::newFromName( $target );
01122                 if ( $userObj instanceof User ) {
01123                         # Note that since numbers are valid usernames, a $target of "12345" will be
01124                         # considered a User.  If you want to pass a block ID, prepend a hash "#12345",
01125                         # since hash characters are not valid in usernames or titles generally.
01126                         return array( $userObj, Block::TYPE_USER );
01127 
01128                 } elseif ( preg_match( '/^#\d+$/', $target ) ) {
01129                         # Autoblock reference in the form "#12345"
01130                         return array( substr( $target, 1 ), Block::TYPE_AUTO );
01131 
01132                 } else {
01133                         # WTF?
01134                         return array( null, null );
01135                 }
01136         }
01137 
01142         public function getType() {
01143                 return $this->mAuto
01144                         ? self::TYPE_AUTO
01145                         : $this->type;
01146         }
01147 
01155         public function getTargetAndType() {
01156                 return array( $this->getTarget(), $this->getType() );
01157         }
01158 
01165         public function getTarget() {
01166                 return $this->target;
01167         }
01168 
01174         public function getExpiry() {
01175                 return $this->mExpiry;
01176         }
01177 
01182         public function setTarget( $target ) {
01183                 list( $this->target, $this->type ) = self::parseTarget( $target );
01184         }
01185 
01190         public function getBlocker() {
01191                 return $this->blocker;
01192         }
01193 
01198         public function setBlocker( $user ) {
01199                 $this->blocker = $user;
01200         }
01201 }