Merge "Remove parameter 'options' from hook 'SkinEditSectionLinks'"
[lhc/web/wiklou.git] / includes / user / User.php
index 11c712a..2f6deb5 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Block\AbstractBlock;
+use MediaWiki\Block\SystemBlock;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Session\SessionManager;
 use MediaWiki\Session\Token;
@@ -278,7 +280,7 @@ class User implements IDBAccessObject, UserIdentity {
        protected $mImplicitGroups;
        /** @var array */
        protected $mFormerGroups;
-       /** @var Block */
+       /** @var AbstractBlock */
        protected $mGlobalBlock;
        /** @var bool */
        protected $mLocked;
@@ -290,13 +292,13 @@ class User implements IDBAccessObject, UserIdentity {
        /** @var WebRequest */
        private $mRequest;
 
-       /** @var Block */
+       /** @var AbstractBlock */
        public $mBlock;
 
        /** @var bool */
        protected $mAllowUsertalk;
 
-       /** @var Block */
+       /** @var AbstractBlock */
        private $mBlockedFromCreateAccount = false;
 
        /** @var int User::READ_* constant bitfield used to load data */
@@ -673,11 +675,20 @@ class User implements IDBAccessObject, UserIdentity {
         * @param int|null $userId User ID, if known
         * @param string|null $userName User name, if known
         * @param int|null $actorId Actor ID, if known
+        * @param bool|string $wikiId remote wiki to which the User/Actor ID applies, or false if none
         * @return User
         */
-       public static function newFromAnyId( $userId, $userName, $actorId ) {
+       public static function newFromAnyId( $userId, $userName, $actorId, $wikiId = false ) {
                global $wgActorTableSchemaMigrationStage;
 
+               // Stop-gap solution for the problem described in T222212.
+               // Force the User ID and Actor ID to zero for users loaded from the database
+               // of another wiki, to prevent subtle data corruption and confusing failure modes.
+               if ( $wikiId !== false ) {
+                       $userId = 0;
+                       $actorId = 0;
+               }
+
                $user = new User;
                $user->mFrom = 'defaults';
 
@@ -914,7 +925,7 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                if ( !( $flags & self::READ_LATEST ) && array_key_exists( $name, self::$idCacheByName ) ) {
-                       return self::$idCacheByName[$name];
+                       return is_null( self::$idCacheByName[$name] ) ? null : (int)self::$idCacheByName[$name];
                }
 
                list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
@@ -1813,13 +1824,14 @@ class User implements IDBAccessObject, UserIdentity {
 
        /**
         * Get blocking information
+        *
+        * TODO: Move this into the BlockManager, along with block-related properties.
+        *
         * @param bool $fromReplica Whether to check the replica DB first.
         *   To improve performance, non-critical checks are done against replica DBs.
         *   Check when actually saving should be done against master.
         */
        private function getBlockedStatus( $fromReplica = true ) {
-               global $wgProxyWhitelist, $wgApplyIpBlocksToXff, $wgSoftBlockRanges;
-
                if ( $this->mBlockedby != -1 ) {
                        return;
                }
@@ -1833,81 +1845,12 @@ class User implements IDBAccessObject, UserIdentity {
                // overwriting mBlockedby, surely?
                $this->load();
 
-               # We only need to worry about passing the IP address to the Block generator if the
-               # user is not immune to autoblocks/hardblocks, and they are the current user so we
-               # know which IP address they're actually coming from
-               $ip = null;
-               $sessionUser = RequestContext::getMain()->getUser();
-               // the session user is set up towards the end of Setup.php. Until then,
-               // assume it's a logged-out user.
-               $globalUserName = $sessionUser->isSafeToLoad()
-                       ? $sessionUser->getName()
-                       : IP::sanitizeIP( $sessionUser->getRequest()->getIP() );
-               if ( $this->getName() === $globalUserName && !$this->isAllowed( 'ipblock-exempt' ) ) {
-                       $ip = $this->getRequest()->getIP();
-               }
-
-               // User/IP blocking
-               $block = Block::newFromTarget( $this, $ip, !$fromReplica );
-
-               // Cookie blocking
-               if ( !$block instanceof Block ) {
-                       $block = $this->getBlockFromCookieValue( $this->getRequest()->getCookie( 'BlockID' ) );
-               }
-
-               // Proxy blocking
-               if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) {
-                       // Local list
-                       if ( self::isLocallyBlockedProxy( $ip ) ) {
-                               $block = new Block( [
-                                       'byText' => wfMessage( 'proxyblocker' )->text(),
-                                       'reason' => wfMessage( 'proxyblockreason' )->plain(),
-                                       'address' => $ip,
-                                       'systemBlock' => 'proxy',
-                               ] );
-                       } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
-                               $block = new Block( [
-                                       'byText' => wfMessage( 'sorbs' )->text(),
-                                       'reason' => wfMessage( 'sorbsreason' )->plain(),
-                                       'address' => $ip,
-                                       'systemBlock' => 'dnsbl',
-                               ] );
-                       }
-               }
-
-               // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
-               if ( !$block instanceof Block
-                       && $wgApplyIpBlocksToXff
-                       && $ip !== null
-                       && !in_array( $ip, $wgProxyWhitelist )
-               ) {
-                       $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
-                       $xff = array_map( 'trim', explode( ',', $xff ) );
-                       $xff = array_diff( $xff, [ $ip ] );
-                       $xffblocks = Block::getBlocksForIPList( $xff, $this->isAnon(), !$fromReplica );
-                       $block = Block::chooseBlock( $xffblocks, $xff );
-                       if ( $block instanceof Block ) {
-                               # Mangle the reason to alert the user that the block
-                               # originated from matching the X-Forwarded-For header.
-                               $block->setReason( wfMessage( 'xffblockreason', $block->getReason() )->plain() );
-                       }
-               }
-
-               if ( !$block instanceof Block
-                       && $ip !== null
-                       && $this->isAnon()
-                       && IP::isInRanges( $ip, $wgSoftBlockRanges )
-               ) {
-                       $block = new Block( [
-                               'address' => $ip,
-                               'byText' => 'MediaWiki default',
-                               'reason' => wfMessage( 'softblockrangesreason', $ip )->plain(),
-                               'anonOnly' => true,
-                               'systemBlock' => 'wgSoftBlockRanges',
-                       ] );
-               }
+               $block = MediaWikiServices::getInstance()->getBlockManager()->getUserBlock(
+                       $this,
+                       $fromReplica
+               );
 
-               if ( $block instanceof Block ) {
+               if ( $block instanceof AbstractBlock ) {
                        wfDebug( __METHOD__ . ": Found block.\n" );
                        $this->mBlock = $block;
                        $this->mBlockedby = $block->getByName();
@@ -1928,82 +1871,30 @@ class User implements IDBAccessObject, UserIdentity {
                Hooks::run( 'GetBlockedStatus', [ &$thisUser ] );
        }
 
-       /**
-        * Try to load a Block from an ID given in a cookie value.
-        * @param string|null $blockCookieVal The cookie value to check.
-        * @return Block|bool The Block object, or false if none could be loaded.
-        */
-       protected function getBlockFromCookieValue( $blockCookieVal ) {
-               // Make sure there's something to check. The cookie value must start with a number.
-               if ( strlen( $blockCookieVal ) < 1 || !is_numeric( substr( $blockCookieVal, 0, 1 ) ) ) {
-                       return false;
-               }
-               // Load the Block from the ID in the cookie.
-               $blockCookieId = Block::getIdFromCookieValue( $blockCookieVal );
-               if ( $blockCookieId !== null ) {
-                       // An ID was found in the cookie.
-                       $tmpBlock = Block::newFromID( $blockCookieId );
-                       if ( $tmpBlock instanceof Block ) {
-                               $config = RequestContext::getMain()->getConfig();
-
-                               switch ( $tmpBlock->getType() ) {
-                                       case Block::TYPE_USER:
-                                               $blockIsValid = !$tmpBlock->isExpired() && $tmpBlock->isAutoblocking();
-                                               $useBlockCookie = ( $config->get( 'CookieSetOnAutoblock' ) === true );
-                                               break;
-                                       case Block::TYPE_IP:
-                                       case Block::TYPE_RANGE:
-                                               // If block is type IP or IP range, load only if user is not logged in (T152462)
-                                               $blockIsValid = !$tmpBlock->isExpired() && !$this->isLoggedIn();
-                                               $useBlockCookie = ( $config->get( 'CookieSetOnIpBlock' ) === true );
-                                               break;
-                                       default:
-                                               $blockIsValid = false;
-                                               $useBlockCookie = false;
-                               }
-
-                               if ( $blockIsValid && $useBlockCookie ) {
-                                       // Use the block.
-                                       return $tmpBlock;
-                               }
-
-                               // If the block is not valid, remove the cookie.
-                               Block::clearCookie( $this->getRequest()->response() );
-                       } else {
-                               // If the block doesn't exist, remove the cookie.
-                               Block::clearCookie( $this->getRequest()->response() );
-                       }
-               }
-               return false;
-       }
-
        /**
         * Whether the given IP is in a DNS blacklist.
         *
+        * @deprecated since 1.34 Use BlockManager::isDnsBlacklisted.
         * @param string $ip IP to check
         * @param bool $checkWhitelist Whether to check the whitelist first
         * @return bool True if blacklisted.
         */
        public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
-               global $wgEnableDnsBlacklist, $wgDnsBlacklistUrls, $wgProxyWhitelist;
-
-               if ( !$wgEnableDnsBlacklist ||
-                       ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) )
-               ) {
-                       return false;
-               }
-
-               return $this->inDnsBlacklist( $ip, $wgDnsBlacklistUrls );
+               return MediaWikiServices::getInstance()->getBlockManager()
+                       ->isDnsBlacklisted( $ip, $checkWhitelist );
        }
 
        /**
         * Whether the given IP is in a given DNS blacklist.
         *
+        * @deprecated since 1.34 Check via BlockManager::isDnsBlacklisted instead.
         * @param string $ip IP to check
         * @param string|array $bases Array of Strings: URL of the DNS blacklist
         * @return bool True if blacklisted.
         */
        public function inDnsBlacklist( $ip, $bases ) {
+               wfDeprecated( __METHOD__, '1.34' );
+
                $found = false;
                // @todo FIXME: IPv6 ???  (https://bugs.php.net/bug.php?id=33170)
                if ( IP::isIPv4( $ip ) ) {
@@ -2045,11 +1936,13 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Check if an IP address is in the local proxy list
         *
+        * @deprecated since 1.34 Use BlockManager::getUserBlock instead.
         * @param string $ip
-        *
         * @return bool
         */
        public static function isLocallyBlockedProxy( $ip ) {
+               wfDeprecated( __METHOD__, '1.34' );
+
                global $wgProxyList;
 
                if ( !$wgProxyList ) {
@@ -2271,7 +2164,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool True if blocked, false otherwise
         */
        public function isBlocked( $fromReplica = true ) {
-               return $this->getBlock( $fromReplica ) instanceof Block &&
+               return $this->getBlock( $fromReplica ) instanceof AbstractBlock &&
                        $this->getBlock()->appliesToRight( 'edit' );
        }
 
@@ -2279,11 +2172,11 @@ class User implements IDBAccessObject, UserIdentity {
         * Get the block affecting the user, or null if the user is not blocked
         *
         * @param bool $fromReplica Whether to check the replica DB instead of the master
-        * @return Block|null
+        * @return AbstractBlock|null
         */
        public function getBlock( $fromReplica = true ) {
                $this->getBlockedStatus( $fromReplica );
-               return $this->mBlock instanceof Block ? $this->mBlock : null;
+               return $this->mBlock instanceof AbstractBlock ? $this->mBlock : null;
        }
 
        /**
@@ -2339,7 +2232,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool True if blocked, false otherwise
         */
        public function isBlockedGlobally( $ip = '' ) {
-               return $this->getGlobalBlock( $ip ) instanceof Block;
+               return $this->getGlobalBlock( $ip ) instanceof AbstractBlock;
        }
 
        /**
@@ -2348,7 +2241,7 @@ class User implements IDBAccessObject, UserIdentity {
         * This is intended for quick UI checks.
         *
         * @param string $ip IP address, uses current client if none given
-        * @return Block|null Block object if blocked, null otherwise
+        * @return AbstractBlock|null Block object if blocked, null otherwise
         * @throws FatalError
         * @throws MWException
         */
@@ -2370,7 +2263,7 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( $blocked && $block === null ) {
                        // back-compat: UserIsBlockedGlobally didn't have $block param first
-                       $block = new Block( [
+                       $block = new SystemBlock( [
                                'address' => $ip,
                                'systemBlock' => 'global-block'
                        ] );
@@ -3783,12 +3676,25 @@ class User implements IDBAccessObject, UserIdentity {
                return true;
        }
 
+       /**
+        * Alias of isLoggedIn() with a name that describes its actual functionality. UserIdentity has
+        * only this new name and not the old isLoggedIn() variant.
+        *
+        * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+        *   anonymous or has no local account (which can happen when importing). This is equivalent to
+        *   getId() != 0 and is provided for code readability.
+        * @since 1.34
+        */
+       public function isRegistered() {
+               return $this->getId() != 0;
+       }
+
        /**
         * Get whether the user is logged in
         * @return bool
         */
        public function isLoggedIn() {
-               return $this->getId() != 0;
+               return $this->isRegistered();
        }
 
        /**
@@ -3796,7 +3702,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool
         */
        public function isAnon() {
-               return !$this->isLoggedIn();
+               return !$this->isRegistered();
        }
 
        /**
@@ -4203,7 +4109,7 @@ class User implements IDBAccessObject, UserIdentity {
                $newTouched = $this->newTouchedTimestamp();
 
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) use ( $newTouched ) {
+               $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
                        global $wgActorTableSchemaMigrationStage;
 
                        $dbw->update( 'user',
@@ -4329,7 +4235,7 @@ class User implements IDBAccessObject, UserIdentity {
                        $fields["user_$name"] = $value;
                }
 
-               return $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) use ( $fields ) {
+               return $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $fields ) {
                        $dbw->insert( 'user', $fields, $fname, [ 'IGNORE' ] );
                        if ( $dbw->affectedRows() ) {
                                $newUser = self::newFromId( $dbw->insertId() );
@@ -4383,7 +4289,7 @@ class User implements IDBAccessObject, UserIdentity {
                $this->mTouched = $this->newTouchedTimestamp();
 
                $dbw = wfGetDB( DB_MASTER );
-               $status = $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) {
+               $status = $dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) {
                        $noPass = PasswordFactory::newInvalidPassword()->toString();
                        $dbw->insert( 'user',
                                [
@@ -4488,7 +4394,7 @@ class User implements IDBAccessObject, UserIdentity {
 
        /**
         * Get whether the user is explicitly blocked from account creation.
-        * @return bool|Block
+        * @return bool|AbstractBlock
         */
        public function isBlockedFromCreateAccount() {
                $this->getBlockedStatus();
@@ -4502,7 +4408,7 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
                        $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
                }
-               return $this->mBlockedFromCreateAccount instanceof Block
+               return $this->mBlockedFromCreateAccount instanceof AbstractBlock
                        && $this->mBlockedFromCreateAccount->appliesToRight( 'createaccount' )
                        ? $this->mBlockedFromCreateAccount
                        : false;