MediaWiki  REL1_20
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 
00236         protected function newLoad( $vagueTarget = null ) {
00237                 $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
00238 
00239                 if( $this->type !== null ){
00240                         $conds = array(
00241                                 'ipb_address' => array( (string)$this->target ),
00242                         );
00243                 } else {
00244                         $conds = array( 'ipb_address' => array() );
00245                 }
00246 
00247                 # Be aware that the != '' check is explicit, since empty values will be
00248                 # passed by some callers (bug 29116)
00249                 if( $vagueTarget != ''){
00250                         list( $target, $type ) = self::parseTarget( $vagueTarget );
00251                         switch( $type ) {
00252                                 case self::TYPE_USER:
00253                                         # Slightly wierd, but who are we to argue?
00254                                         $conds['ipb_address'][] = (string)$target;
00255                                         break;
00256 
00257                                 case self::TYPE_IP:
00258                                         $conds['ipb_address'][] = (string)$target;
00259                                         $conds[] = self::getRangeCond( IP::toHex( $target ) );
00260                                         $conds = $db->makeList( $conds, LIST_OR );
00261                                         break;
00262 
00263                                 case self::TYPE_RANGE:
00264                                         list( $start, $end ) = IP::parseRange( $target );
00265                                         $conds['ipb_address'][] = (string)$target;
00266                                         $conds[] = self::getRangeCond( $start, $end );
00267                                         $conds = $db->makeList( $conds, LIST_OR );
00268                                         break;
00269 
00270                                 default:
00271                                         throw new MWException( "Tried to load block with invalid type" );
00272                         }
00273                 }
00274 
00275                 $res = $db->select( 'ipblocks', self::selectFields(), $conds, __METHOD__ );
00276 
00277                 # This result could contain a block on the user, a block on the IP, and a russian-doll
00278                 # set of rangeblocks.  We want to choose the most specific one, so keep a leader board.
00279                 $bestRow = null;
00280 
00281                 # Lower will be better
00282                 $bestBlockScore = 100;
00283 
00284                 # This is begging for $this = $bestBlock, but that's not allowed in PHP :(
00285                 $bestBlockPreventsEdit = null;
00286 
00287                 foreach( $res as $row ){
00288                         $block = self::newFromRow( $row );
00289 
00290                         # Don't use expired blocks
00291                         if( $block->deleteIfExpired() ){
00292                                 continue;
00293                         }
00294 
00295                         # Don't use anon only blocks on users
00296                         if( $this->type == self::TYPE_USER && !$block->isHardblock() ){
00297                                 continue;
00298                         }
00299 
00300                         if( $block->getType() == self::TYPE_RANGE ){
00301                                 # This is the number of bits that are allowed to vary in the block, give
00302                                 # or take some floating point errors
00303                                 $end = wfBaseconvert( $block->getRangeEnd(), 16, 10 );
00304                                 $start = wfBaseconvert( $block->getRangeStart(), 16, 10 );
00305                                 $size = log( $end - $start + 1, 2 );
00306 
00307                                 # This has the nice property that a /32 block is ranked equally with a
00308                                 # single-IP block, which is exactly what it is...
00309                                 $score = self::TYPE_RANGE  - 1 + ( $size / 128 );
00310 
00311                         } else {
00312                                 $score = $block->getType();
00313                         }
00314 
00315                         if( $score < $bestBlockScore ){
00316                                 $bestBlockScore = $score;
00317                                 $bestRow = $row;
00318                                 $bestBlockPreventsEdit = $block->prevents( 'edit' );
00319                         }
00320                 }
00321 
00322                 if( $bestRow !== null ){
00323                         $this->initFromRow( $bestRow );
00324                         $this->prevents( 'edit', $bestBlockPreventsEdit );
00325                         return true;
00326                 } else {
00327                         return false;
00328                 }
00329         }
00330 
00337         public static function getRangeCond( $start, $end = null ) {
00338                 if ( $end === null ) {
00339                         $end = $start;
00340                 }
00341                 # Per bug 14634, we want to include relevant active rangeblocks; for
00342                 # rangeblocks, we want to include larger ranges which enclose the given
00343                 # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
00344                 # so we can improve performance by filtering on a LIKE clause
00345                 $chunk = self::getIpFragment( $start );
00346                 $dbr = wfGetDB( DB_SLAVE );
00347                 $like = $dbr->buildLike( $chunk, $dbr->anyString() );
00348 
00349                 # Fairly hard to make a malicious SQL statement out of hex characters,
00350                 # but stranger things have happened...
00351                 $safeStart = $dbr->addQuotes( $start );
00352                 $safeEnd = $dbr->addQuotes( $end );
00353 
00354                 return $dbr->makeList(
00355                         array(
00356                                 "ipb_range_start $like",
00357                                 "ipb_range_start <= $safeStart",
00358                                 "ipb_range_end >= $safeEnd",
00359                         ),
00360                         LIST_AND
00361                 );
00362         }
00363 
00370         protected static function getIpFragment( $hex ) {
00371                 global $wgBlockCIDRLimit;
00372                 if ( substr( $hex, 0, 3 ) == 'v6-' ) {
00373                         return 'v6-' . substr( substr( $hex, 3 ), 0,  floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
00374                 } else {
00375                         return substr( $hex, 0,  floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
00376                 }
00377         }
00378 
00384         protected function initFromRow( $row ) {
00385                 $this->setTarget( $row->ipb_address );
00386                 if ( $row->ipb_by ) { // local user
00387                         $this->setBlocker( User::newFromID( $row->ipb_by ) );
00388                 } else { // foreign user
00389                         $this->setBlocker( $row->ipb_by_text );
00390                 }
00391 
00392                 $this->mReason = $row->ipb_reason;
00393                 $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
00394                 $this->mAuto = $row->ipb_auto;
00395                 $this->mHideName = $row->ipb_deleted;
00396                 $this->mId = $row->ipb_id;
00397                 $this->mParentBlockId = $row->ipb_parent_block_id;
00398 
00399                 // I wish I didn't have to do this
00400                 $db = wfGetDB( DB_SLAVE );
00401                 if ( $row->ipb_expiry == $db->getInfinity() ) {
00402                         $this->mExpiry = 'infinity';
00403                 } else {
00404                         $this->mExpiry = wfTimestamp( TS_MW, $row->ipb_expiry );
00405                 }
00406 
00407                 $this->isHardblock( !$row->ipb_anon_only );
00408                 $this->isAutoblocking( $row->ipb_enable_autoblock );
00409 
00410                 $this->prevents( 'createaccount', $row->ipb_create_account );
00411                 $this->prevents( 'sendemail', $row->ipb_block_email );
00412                 $this->prevents( 'editownusertalk', !$row->ipb_allow_usertalk );
00413         }
00414 
00420         public static function newFromRow( $row ){
00421                 $block = new Block;
00422                 $block->initFromRow( $row );
00423                 return $block;
00424         }
00425 
00431         public function delete() {
00432                 if ( wfReadOnly() ) {
00433                         return false;
00434                 }
00435 
00436                 if ( !$this->getId() ) {
00437                         throw new MWException( "Block::delete() requires that the mId member be filled\n" );
00438                 }
00439 
00440                 $dbw = wfGetDB( DB_MASTER );
00441                 $dbw->delete( 'ipblocks', array( 'ipb_parent_block_id' => $this->getId() ), __METHOD__ );
00442                 $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->getId() ), __METHOD__ );
00443 
00444                 return $dbw->affectedRows() > 0;
00445         }
00446 
00455         public function insert( $dbw = null ) {
00456                 wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
00457 
00458                 if ( $dbw === null ) {
00459                         $dbw = wfGetDB( DB_MASTER );
00460                 }
00461 
00462                 # Don't collide with expired blocks
00463                 Block::purgeExpired();
00464 
00465                 $row = $this->getDatabaseArray();
00466                 $row['ipb_id'] = $dbw->nextSequenceValue("ipblocks_ipb_id_seq");
00467 
00468                 $dbw->insert(
00469                         'ipblocks',
00470                         $row,
00471                         __METHOD__,
00472                         array( 'IGNORE' )
00473                 );
00474                 $affected = $dbw->affectedRows();
00475                 $this->mId = $dbw->insertId();
00476 
00477                 if ( $affected ) {
00478                         $auto_ipd_ids = $this->doRetroactiveAutoblock();
00479                         return array( 'id' => $this->mId, 'autoIds' => $auto_ipd_ids );
00480                 }
00481 
00482                 return false;
00483         }
00484 
00492         public function update() {
00493                 wfDebug( "Block::update; timestamp {$this->mTimestamp}\n" );
00494                 $dbw = wfGetDB( DB_MASTER );
00495 
00496                 $dbw->update(
00497                         'ipblocks',
00498                         $this->getDatabaseArray( $dbw ),
00499                         array( 'ipb_id' => $this->getId() ),
00500                         __METHOD__
00501                 );
00502 
00503                 return $dbw->affectedRows();
00504         }
00505 
00511         protected function getDatabaseArray( $db = null ){
00512                 if( !$db ){
00513                         $db = wfGetDB( DB_SLAVE );
00514                 }
00515                 $expiry = $db->encodeExpiry( $this->mExpiry );
00516 
00517                 if ( $this->forcedTargetID ) {
00518                         $uid = $this->forcedTargetID;
00519                 } else {
00520                         $uid = $this->target instanceof User ? $this->target->getID() : 0;
00521                 }
00522 
00523                 $a = array(
00524                         'ipb_address'          => (string)$this->target,
00525                         'ipb_user'             => $uid,
00526                         'ipb_by'               => $this->getBy(),
00527                         'ipb_by_text'          => $this->getByName(),
00528                         'ipb_reason'           => $this->mReason,
00529                         'ipb_timestamp'        => $db->timestamp( $this->mTimestamp ),
00530                         'ipb_auto'             => $this->mAuto,
00531                         'ipb_anon_only'        => !$this->isHardblock(),
00532                         'ipb_create_account'   => $this->prevents( 'createaccount' ),
00533                         'ipb_enable_autoblock' => $this->isAutoblocking(),
00534                         'ipb_expiry'           => $expiry,
00535                         'ipb_range_start'      => $this->getRangeStart(),
00536                         'ipb_range_end'        => $this->getRangeEnd(),
00537                         'ipb_deleted'          => intval( $this->mHideName ), // typecast required for SQLite
00538                         'ipb_block_email'      => $this->prevents( 'sendemail' ),
00539                         'ipb_allow_usertalk'   => !$this->prevents( 'editownusertalk' ),
00540                         'ipb_parent_block_id'            => $this->mParentBlockId
00541                 );
00542 
00543                 return $a;
00544         }
00545 
00552         protected function doRetroactiveAutoblock() {
00553                 $blockIds = array();
00554                 # If autoblock is enabled, autoblock the LAST IP(s) used
00555                 if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
00556                         wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
00557 
00558                         $continue = wfRunHooks(
00559                                 'PerformRetroactiveAutoblock', array( $this, &$blockIds ) );
00560 
00561                         if ( $continue ) {
00562                                 self::defaultRetroactiveAutoblock( $this, $blockIds );
00563                         }
00564                 }
00565                 return $blockIds;
00566         }
00567 
00576         protected static function defaultRetroactiveAutoblock( Block $block, array &$blockIds ) {
00577                 $dbr = wfGetDB( DB_SLAVE );
00578 
00579                 $options = array( 'ORDER BY' => 'rc_timestamp DESC' );
00580                 $conds = array( 'rc_user_text' => (string)$block->getTarget() );
00581 
00582                 // Just the last IP used.
00583                 $options['LIMIT'] = 1;
00584 
00585                 $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
00586                         __METHOD__ ,  $options );
00587 
00588                 if ( !$dbr->numRows( $res ) ) {
00589                         # No results, don't autoblock anything
00590                         wfDebug( "No IP found to retroactively autoblock\n" );
00591                 } else {
00592                         foreach ( $res as $row ) {
00593                                 if ( $row->rc_ip ) {
00594                                         $id = $block->doAutoblock( $row->rc_ip );
00595                                         if ( $id ) $blockIds[] = $id;
00596                                 }
00597                         }
00598                 }
00599         }
00600 
00608         public static function isWhitelistedFromAutoblocks( $ip ) {
00609                 global $wgMemc;
00610 
00611                 // Try to get the autoblock_whitelist from the cache, as it's faster
00612                 // than getting the msg raw and explode()'ing it.
00613                 $key = wfMemcKey( 'ipb', 'autoblock', 'whitelist' );
00614                 $lines = $wgMemc->get( $key );
00615                 if ( !$lines ) {
00616                         $lines = explode( "\n", wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() );
00617                         $wgMemc->set( $key, $lines, 3600 * 24 );
00618                 }
00619 
00620                 wfDebug( "Checking the autoblock whitelist..\n" );
00621 
00622                 foreach ( $lines as $line ) {
00623                         # List items only
00624                         if ( substr( $line, 0, 1 ) !== '*' ) {
00625                                 continue;
00626                         }
00627 
00628                         $wlEntry = substr( $line, 1 );
00629                         $wlEntry = trim( $wlEntry );
00630 
00631                         wfDebug( "Checking $ip against $wlEntry..." );
00632 
00633                         # Is the IP in this range?
00634                         if ( IP::isInRange( $ip, $wlEntry ) ) {
00635                                 wfDebug( " IP $ip matches $wlEntry, not autoblocking\n" );
00636                                 return true;
00637                         } else {
00638                                 wfDebug( " No match\n" );
00639                         }
00640                 }
00641 
00642                 return false;
00643         }
00644 
00651         public function doAutoblock( $autoblockIP ) {
00652                 # If autoblocks are disabled, go away.
00653                 if ( !$this->isAutoblocking() ) {
00654                         return false;
00655                 }
00656 
00657                 # Check for presence on the autoblock whitelist.
00658                 if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
00659                         return false;
00660                 }
00661 
00662                 # Allow hooks to cancel the autoblock.
00663                 if ( !wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
00664                         wfDebug( "Autoblock aborted by hook.\n" );
00665                         return false;
00666                 }
00667 
00668                 # It's okay to autoblock. Go ahead and insert/update the block...
00669 
00670                 # Do not add a *new* block if the IP is already blocked.
00671                 $ipblock = Block::newFromTarget( $autoblockIP );
00672                 if ( $ipblock ) {
00673                         # Check if the block is an autoblock and would exceed the user block
00674                         # if renewed. If so, do nothing, otherwise prolong the block time...
00675                         if ( $ipblock->mAuto && // @TODO: why not compare $ipblock->mExpiry?
00676                                 $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
00677                         ) {
00678                                 # Reset block timestamp to now and its expiry to
00679                                 # $wgAutoblockExpiry in the future
00680                                 $ipblock->updateTimestamp();
00681                         }
00682                         return false;
00683                 }
00684 
00685                 # Make a new block object with the desired properties.
00686                 $autoblock = new Block;
00687                 wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
00688                 $autoblock->setTarget( $autoblockIP );
00689                 $autoblock->setBlocker( $this->getBlocker() );
00690                 $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )->inContentLanguage()->text();
00691                 $timestamp = wfTimestampNow();
00692                 $autoblock->mTimestamp = $timestamp;
00693                 $autoblock->mAuto = 1;
00694                 $autoblock->prevents( 'createaccount', $this->prevents( 'createaccount' ) );
00695                 # Continue suppressing the name if needed
00696                 $autoblock->mHideName = $this->mHideName;
00697                 $autoblock->prevents( 'editownusertalk', $this->prevents( 'editownusertalk' ) );
00698                 $autoblock->mParentBlockId = $this->mId;
00699 
00700                 if ( $this->mExpiry == 'infinity' ) {
00701                         # Original block was indefinite, start an autoblock now
00702                         $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
00703                 } else {
00704                         # If the user is already blocked with an expiry date, we don't
00705                         # want to pile on top of that.
00706                         $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
00707                 }
00708 
00709                 # Insert the block...
00710                 $status = $autoblock->insert();
00711                 return $status
00712                         ? $status['id']
00713                         : false;
00714         }
00715 
00720         public function deleteIfExpired() {
00721                 wfProfileIn( __METHOD__ );
00722 
00723                 if ( $this->isExpired() ) {
00724                         wfDebug( "Block::deleteIfExpired() -- deleting\n" );
00725                         $this->delete();
00726                         $retVal = true;
00727                 } else {
00728                         wfDebug( "Block::deleteIfExpired() -- not expired\n" );
00729                         $retVal = false;
00730                 }
00731 
00732                 wfProfileOut( __METHOD__ );
00733                 return $retVal;
00734         }
00735 
00740         public function isExpired() {
00741                 $timestamp = wfTimestampNow();
00742                 wfDebug( "Block::isExpired() checking current " . $timestamp . " vs $this->mExpiry\n" );
00743 
00744                 if ( !$this->mExpiry ) {
00745                         return false;
00746                 } else {
00747                         return $timestamp > $this->mExpiry;
00748                 }
00749         }
00750 
00755         public function isValid() {
00756                 return $this->getTarget() != null;
00757         }
00758 
00762         public function updateTimestamp() {
00763                 if ( $this->mAuto ) {
00764                         $this->mTimestamp = wfTimestamp();
00765                         $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
00766 
00767                         $dbw = wfGetDB( DB_MASTER );
00768                         $dbw->update( 'ipblocks',
00769                                 array( /* SET */
00770                                         'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
00771                                         'ipb_expiry' => $dbw->timestamp( $this->mExpiry ),
00772                                 ),
00773                                 array( /* WHERE */
00774                                         'ipb_address' => (string)$this->getTarget()
00775                                 ),
00776                                 __METHOD__
00777                         );
00778                 }
00779         }
00780 
00785         public function getRangeStart() {
00786                 switch( $this->type ) {
00787                         case self::TYPE_USER:
00788                                 return '';
00789                         case self::TYPE_IP:
00790                                 return IP::toHex( $this->target );
00791                         case self::TYPE_RANGE:
00792                                 list( $start, /*...*/ ) = IP::parseRange( $this->target );
00793                                 return $start;
00794                         default: throw new MWException( "Block with invalid type" );
00795                 }
00796         }
00797 
00802         public function getRangeEnd() {
00803                 switch( $this->type ) {
00804                         case self::TYPE_USER:
00805                                 return '';
00806                         case self::TYPE_IP:
00807                                 return IP::toHex( $this->target );
00808                         case self::TYPE_RANGE:
00809                                 list( /*...*/, $end ) = IP::parseRange( $this->target );
00810                                 return $end;
00811                         default: throw new MWException( "Block with invalid type" );
00812                 }
00813         }
00814 
00820         public function getBy() {
00821                 $blocker = $this->getBlocker();
00822                 return ( $blocker instanceof User )
00823                         ? $blocker->getId()
00824                         : 0;
00825         }
00826 
00832         public function getByName() {
00833                 $blocker = $this->getBlocker();
00834                 return ( $blocker instanceof User )
00835                         ? $blocker->getName()
00836                         : (string)$blocker; // username
00837         }
00838 
00843         public function getId() {
00844                 return $this->mId;
00845         }
00846 
00853         public function forUpdate( $x = null ) {
00854                 wfDeprecated( __METHOD__, '1.18' );
00855                 # noop
00856         }
00857 
00864         public function fromMaster( $x = null ) {
00865                 return wfSetVar( $this->mFromMaster, $x );
00866         }
00867 
00873         public function isHardblock( $x = null ) {
00874                 wfSetVar( $this->isHardblock, $x );
00875 
00876                 # You can't *not* hardblock a user
00877                 return $this->getType() == self::TYPE_USER
00878                         ? true
00879                         : $this->isHardblock;
00880         }
00881 
00882         public function isAutoblocking( $x = null ) {
00883                 wfSetVar( $this->isAutoblocking, $x );
00884 
00885                 # You can't put an autoblock on an IP or range as we don't have any history to
00886                 # look over to get more IPs from
00887                 return $this->getType() == self::TYPE_USER
00888                         ? $this->isAutoblocking
00889                         : false;
00890         }
00891 
00898         public function prevents( $action, $x = null ) {
00899                 switch( $action ) {
00900                         case 'edit':
00901                                 # For now... <evil laugh>
00902                                 return true;
00903 
00904                         case 'createaccount':
00905                                 return wfSetVar( $this->mCreateAccount, $x );
00906 
00907                         case 'sendemail':
00908                                 return wfSetVar( $this->mBlockEmail, $x );
00909 
00910                         case 'editownusertalk':
00911                                 return wfSetVar( $this->mDisableUsertalk, $x );
00912 
00913                         default:
00914                                 return null;
00915                 }
00916         }
00917 
00922         public function getRedactedName() {
00923                 if ( $this->mAuto ) {
00924                         return Html::rawElement(
00925                                 'span',
00926                                 array( 'class' => 'mw-autoblockid' ),
00927                                 wfMessage( 'autoblockid', $this->mId )
00928                         );
00929                 } else {
00930                         return htmlspecialchars( $this->getTarget() );
00931                 }
00932         }
00933 
00942         public static function encodeExpiry( $expiry, $db ) {
00943                 wfDeprecated( __METHOD__, '1.18' );
00944                 return $db->encodeExpiry( $expiry );
00945         }
00946 
00955         public static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
00956                 wfDeprecated( __METHOD__, '1.18' );
00957                 global $wgContLang;
00958                 return $wgContLang->formatExpiry( $expiry, $timestampType );
00959         }
00960 
00967         public static function getAutoblockExpiry( $timestamp ) {
00968                 global $wgAutoblockExpiry;
00969 
00970                 return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
00971         }
00972 
00980         public static function normaliseRange( $range ) {
00981                 wfDeprecated( __METHOD__, '1.18' );
00982                 return IP::sanitizeRange( $range );
00983         }
00984 
00988         public static function purgeExpired() {
00989                 $dbw = wfGetDB( DB_MASTER );
00990                 $dbw->delete( 'ipblocks',
00991                         array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
00992         }
00993 
01000         public static function infinity() {
01001                 wfDeprecated( __METHOD__, '1.18' );
01002                 return wfGetDB( DB_SLAVE )->getInfinity();
01003         }
01004 
01012         public static function parseExpiryInput( $expiry ) {
01013                 wfDeprecated( __METHOD__, '1.18' );
01014                 return SpecialBlock::parseExpiryInput( $expiry );
01015         }
01016 
01037         public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
01038 
01039                 list( $target, $type ) = self::parseTarget( $specificTarget );
01040                 if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ){
01041                         return Block::newFromID( $target );
01042 
01043                 } elseif( $target === null && $vagueTarget == '' ){
01044                         # We're not going to find anything useful here
01045                         # Be aware that the == '' check is explicit, since empty values will be
01046                         # passed by some callers (bug 29116)
01047                         return null;
01048 
01049                 } elseif( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE ) ) ) {
01050                         $block = new Block();
01051                         $block->fromMaster( $fromMaster );
01052 
01053                         if( $type !== null ){
01054                                 $block->setTarget( $target );
01055                         }
01056 
01057                         if( $block->newLoad( $vagueTarget ) ){
01058                                 return $block;
01059                         }
01060                 }
01061                 return null;
01062         }
01063 
01072         public static function parseTarget( $target ) {
01073                 # We may have been through this before
01074                 if( $target instanceof User ){
01075                         if( IP::isValid( $target->getName() ) ){
01076                                 return array( $target, self::TYPE_IP );
01077                         } else {
01078                                 return array( $target, self::TYPE_USER );
01079                         }
01080                 } elseif( $target === null ){
01081                         return array( null, null );
01082                 }
01083 
01084                 $target = trim( $target );
01085 
01086                 if ( IP::isValid( $target ) ) {
01087                         # We can still create a User if it's an IP address, but we need to turn
01088                         # off validation checking (which would exclude IP addresses)
01089                         return array(
01090                                 User::newFromName( IP::sanitizeIP( $target ), false ),
01091                                 Block::TYPE_IP
01092                         );
01093 
01094                 } elseif ( IP::isValidBlock( $target ) ) {
01095                         # Can't create a User from an IP range
01096                         return array( IP::sanitizeRange( $target ), Block::TYPE_RANGE );
01097                 }
01098 
01099                 # Consider the possibility that this is not a username at all
01100                 # but actually an old subpage (bug #29797)
01101                 if( strpos( $target, '/' ) !== false ){
01102                         # An old subpage, drill down to the user behind it
01103                         $parts = explode( '/', $target );
01104                         $target = $parts[0];
01105                 }
01106 
01107                 $userObj = User::newFromName( $target );
01108                 if ( $userObj instanceof User ) {
01109                         # Note that since numbers are valid usernames, a $target of "12345" will be
01110                         # considered a User.  If you want to pass a block ID, prepend a hash "#12345",
01111                         # since hash characters are not valid in usernames or titles generally.
01112                         return array( $userObj, Block::TYPE_USER );
01113 
01114                 } elseif ( preg_match( '/^#\d+$/', $target ) ) {
01115                         # Autoblock reference in the form "#12345"
01116                         return array( substr( $target, 1 ), Block::TYPE_AUTO );
01117 
01118                 } else {
01119                         # WTF?
01120                         return array( null, null );
01121                 }
01122         }
01123 
01128         public function getType() {
01129                 return $this->mAuto
01130                         ? self::TYPE_AUTO
01131                         : $this->type;
01132         }
01133 
01141         public function getTargetAndType() {
01142                 return array( $this->getTarget(), $this->getType() );
01143         }
01144 
01151         public function getTarget() {
01152                 return $this->target;
01153         }
01154 
01160         public function getExpiry() {
01161                 return $this->mExpiry;
01162         }
01163 
01168         public function setTarget( $target ){
01169                 list( $this->target, $this->type ) = self::parseTarget( $target );
01170         }
01171 
01176         public function getBlocker(){
01177                 return $this->blocker;
01178         }
01179 
01184         public function setBlocker( $user ){
01185                 $this->blocker = $user;
01186         }
01187 }