Deprecated $wgUDPProfilerHost, $wgUDPProfilerPort and $wgUDPProfilerFormatString
[lhc/web/wiklou.git] / includes / User.php
index e25bc1f..5348020 100644 (file)
@@ -112,6 +112,7 @@ class User implements IDBAccessObject {
                'deletelogentry',
                'deleterevision',
                'edit',
+               'editcontentmodel',
                'editinterface',
                'editprotected',
                'editmyoptions',
@@ -641,10 +642,11 @@ class User implements IDBAccessObject {
                global $wgContLang, $wgMaxNameChars;
 
                if ( $name == ''
-               || User::isIP( $name )
-               || strpos( $name, '/' ) !== false
-               || strlen( $name ) > $wgMaxNameChars
-               || $name != $wgContLang->ucfirst( $name ) ) {
+                       || User::isIP( $name )
+                       || strpos( $name, '/' ) !== false
+                       || strlen( $name ) > $wgMaxNameChars
+                       || $name != $wgContLang->ucfirst( $name )
+               ) {
                        wfDebugLog( 'username', __METHOD__ .
                                ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
                        return false;
@@ -1752,7 +1754,9 @@ class User implements IDBAccessObject {
                // If more than one group applies, use the group with the highest limit
                foreach ( $this->getGroups() as $group ) {
                        if ( isset( $limits[$group] ) ) {
-                               if ( $userLimit === false || $limits[$group] > $userLimit ) {
+                               if ( $userLimit === false
+                                       || $limits[$group][0] / $limits[$group][1] > $userLimit[0] / $userLimit[1]
+                               ) {
                                        $userLimit = $limits[$group];
                                }
                        }
@@ -3815,7 +3819,6 @@ class User implements IDBAccessObject {
                        return false;
                }
 
-               $passwordFactory = self::getPasswordFactory();
                if ( !$this->mPassword->equals( $password ) ) {
                        if ( $wgLegacyEncoding ) {
                                // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
@@ -3829,6 +3832,7 @@ class User implements IDBAccessObject {
                        }
                }
 
+               $passwordFactory = self::getPasswordFactory();
                if ( $passwordFactory->needsUpdate( $this->mPassword ) ) {
                        $this->mPassword = $passwordFactory->newFromPlaintext( $password );
                        $this->saveSettings();
@@ -3875,22 +3879,15 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Initialize (if necessary) and return a session token value
-        * which can be used in edit forms to show that the user's
-        * login credentials aren't being hijacked with a foreign form
-        * submission.
-        *
-        * @since 1.19
+        * Internal implementation for self::getEditToken() and
+        * self::matchEditToken().
         *
-        * @param string|array $salt Array of Strings Optional function-specific data for hashing
-        * @param WebRequest|null $request WebRequest object to use or null to use $wgRequest
-        * @return string The new edit token
+        * @param string|array $salt
+        * @param WebRequest $request
+        * @param string|int $timestamp
+        * @return string
         */
-       public function getEditToken( $salt = '', $request = null ) {
-               if ( $request == null ) {
-                       $request = $this->getRequest();
-               }
-
+       private function getEditTokenAtTimestamp( $salt, $request, $timestamp ) {
                if ( $this->isAnon() ) {
                        return self::EDIT_TOKEN_SUFFIX;
                } else {
@@ -3902,10 +3899,30 @@ class User implements IDBAccessObject {
                        if ( is_array( $salt ) ) {
                                $salt = implode( '|', $salt );
                        }
-                       return md5( $token . $salt ) . self::EDIT_TOKEN_SUFFIX;
+                       return hash_hmac( 'md5', $timestamp . $salt, $token, false ) .
+                               dechex( $timestamp ) .
+                               self::EDIT_TOKEN_SUFFIX;
                }
        }
 
+       /**
+        * Initialize (if necessary) and return a session token value
+        * which can be used in edit forms to show that the user's
+        * login credentials aren't being hijacked with a foreign form
+        * submission.
+        *
+        * @since 1.19
+        *
+        * @param string|array $salt Array of Strings Optional function-specific data for hashing
+        * @param WebRequest|null $request WebRequest object to use or null to use $wgRequest
+        * @return string The new edit token
+        */
+       public function getEditToken( $salt = '', $request = null ) {
+               return $this->getEditTokenAtTimestamp(
+                       $salt, $request ?: $this->getRequest(), wfTimestamp()
+               );
+       }
+
        /**
         * Generate a looking random token for various uses.
         *
@@ -3926,15 +3943,34 @@ class User implements IDBAccessObject {
         * @param string $val Input value to compare
         * @param string $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
+        * @param int $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
-       public function matchEditToken( $val, $salt = '', $request = null ) {
-               $sessionToken = $this->getEditToken( $salt, $request );
+       public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
+               if ( $this->isAnon() ) {
+                       return $val === self::EDIT_TOKEN_SUFFIX;
+               }
+
+               $suffixLen = strlen( self::EDIT_TOKEN_SUFFIX );
+               if ( strlen( $val ) <= 32 + $suffixLen ) {
+                       return false;
+               }
+
+               $timestamp = hexdec( substr( $val, 32, -$suffixLen ) );
+               if ( $maxage !== null && $timestamp < wfTimestamp() - $maxage ) {
+                       // Expired token
+                       return false;
+               }
+
+               $sessionToken = $this->getEditTokenAtTimestamp(
+                       $salt, $request ?: $this->getRequest(), $timestamp
+               );
+
                if ( $val != $sessionToken ) {
                        wfDebug( "User::matchEditToken: broken session data\n" );
                }
 
-               return $val == $sessionToken;
+               return hash_equals( $sessionToken, $val );
        }
 
        /**
@@ -3944,11 +3980,12 @@ class User implements IDBAccessObject {
         * @param string $val Input value to compare
         * @param string $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
+        * @param int $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
-       public function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
-               $sessionToken = $this->getEditToken( $salt, $request );
-               return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
+       public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
+               $val = substr( $val, 0, strspn( $val, '0123456789abcdef' ) ) . self::EDIT_TOKEN_SUFFIX;
+               return $this->matchEditToken( $val, $salt, $request, $maxage );
        }
 
        /**
@@ -4382,8 +4419,8 @@ class User implements IDBAccessObject {
                global $wgImplicitGroups;
 
                $groups = $wgImplicitGroups;
-               # Deprecated, use $wgImplictGroups instead
-               wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) );
+               # Deprecated, use $wgImplicitGroups instead
+               wfRunHooks( 'UserGetImplicitGroups', array( &$groups ), '1.25' );
 
                return $groups;
        }
@@ -4439,7 +4476,7 @@ class User implements IDBAccessObject {
                }
                $title = self::getGroupPage( $group );
                if ( $title ) {
-                       $page = $title->getPrefixedText();
+                       $page = $title->getFullText();
                        return "[[$page|$text]]";
                } else {
                        return $text;
@@ -4476,6 +4513,7 @@ class User implements IDBAccessObject {
 
                // Same thing for remove
                if ( empty( $wgRemoveGroups[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgRemoveGroups[$group] === true ) {
                        $groups['remove'] = self::getAllGroups();
                } elseif ( is_array( $wgRemoveGroups[$group] ) ) {
@@ -4501,6 +4539,7 @@ class User implements IDBAccessObject {
 
                // Now figure out what groups the user can add to him/herself
                if ( empty( $wgGroupsAddToSelf[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgGroupsAddToSelf[$group] === true ) {
                        // No idea WHY this would be used, but it's there
                        $groups['add-self'] = User::getAllGroups();
@@ -4509,6 +4548,7 @@ class User implements IDBAccessObject {
                }
 
                if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
+                       // Do nothing
                } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
                        $groups['remove-self'] = User::getAllGroups();
                } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {