new files for oracle
[lhc/web/wiklou.git] / includes / User.php
index 28460de..c285bc8 100644 (file)
@@ -2,14 +2,13 @@
 /**
  * See user.txt
  *
- * @package MediaWiki
  */
 
 # Number of characters in user_token field
 define( 'USER_TOKEN_LENGTH', 32 );
 
 # Serialized record version
-define( 'MW_USER_VERSION', 4 );
+define( 'MW_USER_VERSION', 5 );
 
 # Some punctuation to prevent editing from broken text-mangling proxies.
 # FIXME: this is embedded unescaped into HTML attributes in various
@@ -25,7 +24,6 @@ class PasswordError extends MWException {
 
 /**
  *
- * @package MediaWiki
  */
 class User {
 
@@ -95,7 +93,7 @@ class User {
                'mEmailToken',
                'mEmailTokenExpires',
                'mRegistration',
-               
+               'mEditCount',
                # user_group table
                'mGroups',
        );
@@ -189,7 +187,6 @@ class User {
                # Try cache
                $key = wfMemcKey( 'user', 'id', $this->mId );
                $data = $wgMemc->get( $key );
-               
                if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
                        # Object is expired, load from DB
                        $data = false;
@@ -271,7 +268,7 @@ class User {
         * @static
         */
        static function newFromConfirmationCode( $code ) {
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                $id = $dbr->selectField( 'user', 'user_id', array(
                        'user_email_token' => md5( $code ),
                        'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
@@ -303,7 +300,7 @@ class User {
         * @static
         */
        static function whoIs( $id ) {
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
        }
 
@@ -314,7 +311,7 @@ class User {
         * @static
         */
        static function whoIsReal( $id ) {
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), 'User::whoIsReal' );
        }
 
@@ -330,7 +327,7 @@ class User {
                        # Illegal name
                        return null;
                }
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), __METHOD__ );
 
                if ( $s === false ) {
@@ -360,7 +357,7 @@ class User {
         * @return bool
         */
        static function isIP( $name ) {
-               return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name);
+               return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name) || User::isIPv6($name);
                /*return preg_match("/^
                        (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
                        (?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
@@ -369,6 +366,27 @@ class User {
                $/x", $name);*/
        }
 
+       /**
+        * Check if $name is an IPv6 IP.
+        */
+       static function isIPv6($name) {
+               /* 
+                * if it has any non-valid characters, it can't be a valid IPv6  
+                * address.
+                */
+               if (preg_match("/[^:a-fA-F0-9]/", $name))
+                       return false;
+
+               $parts = explode(":", $name);
+               if (count($parts) < 3)
+                       return false;
+               foreach ($parts as $part) {
+                       if (!preg_match("/^[0-9a-fA-F]{0,4}$/", $part))
+                               return false;
+               }
+               return true;
+       }
+
        /**
         * Is the input a valid username?
         *
@@ -467,7 +485,11 @@ class User {
         */
        static function isValidPassword( $password ) {
                global $wgMinimalPasswordLength;
-               return strlen( $password ) >= $wgMinimalPasswordLength;
+
+               $result = null;
+               if( !wfRunHooks( 'isValidPassword', array( $password, &$result ) ) ) return $result;
+               if ($result === false) return false; 
+               return (strlen( $password ) >= $wgMinimalPasswordLength);
        }
 
        /**
@@ -542,13 +564,15 @@ class User {
        /**
         * Count the number of edits of a user
         *
+        * It should not be static and some day should be merged as proper member function / deprecated -- domas
+        * 
         * @param int $uid The user ID to check
         * @return int
         * @static
         */
        static function edits( $uid ) {
-               $dbr =& wfGetDB( DB_SLAVE );
-
+               wfProfileIn( __METHOD__ );
+               $dbr = wfGetDB( DB_SLAVE );
                // check if the user_editcount field has been initialized
                $field = $dbr->selectField(
                        'user', 'user_editcount',
@@ -557,7 +581,7 @@ class User {
                );
 
                if( $field === null ) { // it has not been initialized. do so.
-                       $dbw =& wfGetDb( DB_MASTER );
+                       $dbw = wfGetDb( DB_MASTER );
                        $count = $dbr->selectField(
                                'revision', 'count(*)',
                                array( 'rev_user' => $uid ),
@@ -569,10 +593,11 @@ class User {
                                array( 'user_id' => $uid ),
                                __METHOD__
                        );
-                       return $count;
                } else {
-                       return $field;
+                       $count = $field;
                }
+               wfProfileOut( __METHOD__ );
+               return $count;
        }
 
        /**
@@ -720,7 +745,7 @@ class User {
                        return false;
                }
 
-               $dbr =& wfGetDB( DB_MASTER );
+               $dbr = wfGetDB( DB_MASTER );
                $s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
 
                if ( $s !== false ) {
@@ -738,6 +763,8 @@ class User {
                        $this->mEmailToken = $s->user_email_token;
                        $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $s->user_email_token_expires );
                        $this->mRegistration = wfTimestampOrNull( TS_MW, $s->user_registration );
+                       $this->mEditCount = $s->user_editcount; 
+                       $this->getEditCount(); // revalidation for nulls
 
                        # Load group data
                        $res = $dbr->select( 'user_groups',
@@ -937,6 +964,16 @@ class User {
                return $found;
        }
 
+       /**
+        * Is this user subject to rate limiting?
+        *
+        * @return bool
+        */
+       public function isPingLimitable() {
+               global $wgRateLimitsExcludedGroups;
+               return array_intersect($this->getEffectiveGroups(), $wgRateLimitsExcludedGroups) != array();
+       }
+
        /**
         * Primitive rate limits: enforce maximum actions per time period
         * to put a brake on flooding.
@@ -948,24 +985,22 @@ class User {
         * @public
         */
        function pingLimiter( $action='edit' ) {
-       
+
                # Call the 'PingLimiter' hook
                $result = false;
                if( !wfRunHooks( 'PingLimiter', array( &$this, $action, $result ) ) ) {
                        return $result;
                }
-               
+
                global $wgRateLimits, $wgRateLimitsExcludedGroups;
                if( !isset( $wgRateLimits[$action] ) ) {
                        return false;
                }
-               
+
                # Some groups shouldn't trigger the ping limiter, ever
-               foreach( $this->getGroups() as $group ) {
-                       if( array_search( $group, $wgRateLimitsExcludedGroups ) !== false )
-                               return false;
-               }
-               
+               if( !$this->isPingLimitable() )
+                       return false;
+
                global $wgMemc, $wgRateLimitLog;
                wfProfileIn( __METHOD__ );
 
@@ -1183,7 +1218,7 @@ class User {
         * @private
         */
        function checkNewtalk( $field, $id ) {
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                $ok = $dbr->selectField( 'user_newtalk', $field,
                        array( $field => $id ), __METHOD__ );
                return $ok !== false;
@@ -1200,7 +1235,7 @@ class User {
                        wfDebug( __METHOD__." already set ($field, $id), ignoring\n" );
                        return false;
                }
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $dbw->insert( 'user_newtalk',
                        array( $field => $id ),
                        __METHOD__,
@@ -1220,7 +1255,7 @@ class User {
                        wfDebug( __METHOD__.": already gone ($field, $id), ignoring\n" );
                        return false;
                }
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $dbw->delete( 'user_newtalk',
                        array( $field => $id ),
                        __METHOD__ );
@@ -1305,7 +1340,7 @@ class User {
                if( $this->mId ) {
                        $this->mTouched = self::newTouchedTimestamp();
                        
-                       $dbw =& wfGetDB( DB_MASTER );
+                       $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'user',
                                array( 'user_touched' => $dbw->timestamp( $this->mTouched ) ),
                                array( 'user_id' => $this->mId ),
@@ -1359,11 +1394,23 @@ class User {
                                        $wgMinimalPasswordLength ) );
                        }
                }
-               
+
                if( !$wgAuth->setPassword( $this, $str ) ) {
                        throw new PasswordError( wfMsg( 'externaldberror' ) );
                }
                
+               $this->setInternalPassword( $str );
+
+               return true;
+       }
+
+       /**
+        * Set the password and reset the random token no matter
+        * what.
+        *
+        * @param string $str
+        */
+       function setInternalPassword( $str ) {
                $this->load();
                $this->setToken();
                
@@ -1375,10 +1422,7 @@ class User {
                }
                $this->mNewpassword = '';
                $this->mNewpassTime = null;
-               
-               return true;
        }
-
        /**
         * Set the random token (used for persistent authentication)
         * Called from loadDefaults() among other places.
@@ -1567,12 +1611,12 @@ class User {
                        if( $this->mId ) {
                                $this->mEffectiveGroups[] = 'user';
                                
-                               global $wgAutoConfirmAge;
+                               global $wgAutoConfirmAge, $wgAutoConfirmCount;
+
                                $accountAge = time() - wfTimestampOrNull( TS_UNIX, $this->mRegistration );
-                               if( $accountAge >= $wgAutoConfirmAge ) {
+                               if( $accountAge >= $wgAutoConfirmAge && $this->getEditCount() >= $wgAutoConfirmCount ) {
                                        $this->mEffectiveGroups[] = 'autoconfirmed';
                                }
-                               
                                # Implicit group for users whose email addresses are confirmed
                                global $wgEmailAuthentication;
                                if( self::isValidEmailAddr( $this->mEmail ) ) {
@@ -1587,7 +1631,21 @@ class User {
                }
                return $this->mEffectiveGroups;
        }
-
+       
+       /* Return the edit count for the user. This is where User::edits should have been */
+       function getEditCount() {
+               if ($this->mId) {
+                       if ( !isset( $this->mEditCount ) ) {
+                               /* Populate the count, if it has not been populated yet */
+                               $this->mEditCount = User::edits($this->mId);
+                       } 
+                       return $this->mEditCount;
+               } else {
+                       /* nil */
+                       return null;
+               }
+       }
+       
        /**
         * Add the user to the given group.
         * This takes immediate effect.
@@ -1595,7 +1653,7 @@ class User {
         */
        function addGroup( $group ) {
                $this->load();
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                if( $this->getId() ) {
                        $dbw->insert( 'user_groups',
                                array(
@@ -1619,7 +1677,7 @@ class User {
         */
        function removeGroup( $group ) {
                $this->load();
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $dbw->delete( 'user_groups',
                        array(
                                'ug_user'  => $this->getID(),
@@ -1771,7 +1829,7 @@ class User {
                // If the page is watched by the user (or may be watched), update the timestamp on any
                // any matching rows
                if ( $watched ) {
-                       $dbw =& wfGetDB( DB_MASTER );
+                       $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'watchlist',
                                        array( /* SET */
                                                'wl_notificationtimestamp' => NULL
@@ -1802,7 +1860,7 @@ class User {
                }
                if( $currentUser != 0 )  {
 
-                       $dbw =& wfGetDB( DB_MASTER );
+                       $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'watchlist',
                                array( /* SET */
                                        'wl_notificationtimestamp' => NULL
@@ -1895,7 +1953,7 @@ class User {
                
                $this->mTouched = self::newTouchedTimestamp();
 
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $dbw->update( 'user',
                        array( /* SET */
                                'user_name' => $this->mName,
@@ -1923,7 +1981,7 @@ class User {
                $s = trim( $this->getName() );
                if ( 0 == strcmp( '', $s ) ) return 0;
 
-               $dbr =& wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_SLAVE );
                $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
                if ( $id === false ) {
                        $id = 0;
@@ -1954,7 +2012,7 @@ class User {
                        $user->mOptions = $params['options'] + $user->mOptions;
                        unset( $params['options'] );
                }
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
                $fields = array(
                        'user_id' => $seqVal,
@@ -1987,7 +2045,7 @@ class User {
         */
        function addToDatabase() {
                $this->load();
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
                $dbw->insert( 'user',
                        array(
@@ -2117,7 +2175,7 @@ class User {
                if ( isset( $res ) )
                        return $res;
                else {
-                       $dbr =& wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_SLAVE );
                        return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
                }
        }
@@ -2293,7 +2351,7 @@ class User {
                $token = $this->generateToken( $this->mId . $this->mEmail . $expires );
                $hash = md5( $token );
 
-               $dbw =& wfGetDB( DB_MASTER );
+               $dbw = wfGetDB( DB_MASTER );
                $dbw->update( 'user',
                        array( 'user_email_token'         => $hash,
                               'user_email_token_expires' => $dbw->timestamp( $expires ) ),
@@ -2476,7 +2534,7 @@ class User {
                if( $title ) {
                        global $wgUser;
                        $sk = $wgUser->getSkin();
-                       return $sk->makeLinkObj( $title, $text );
+                       return $sk->makeLinkObj( $title, htmlspecialchars( $text ) );
                } else {
                        return $text;
                }
@@ -2542,6 +2600,8 @@ class User {
                                        __METHOD__ );
                        }
                }
+               // edit count in user cache too
+               $this->invalidateCache();
        }
 }