* @package MediaWiki not Mediawiki
[lhc/web/wiklou.git] / includes / User.php
index 5f0260d..018324f 100644 (file)
@@ -153,7 +153,7 @@ class User {
                $fname = 'User::loadDefaults' . $n;
                wfProfileIn( $fname );
                
-               global $wgContLang, $wgIP;
+               global $wgContLang, $wgIP, $wgDBname;
                global $wgNamespacesToBeSearchedDefault;
 
                $this->mId = 0;
@@ -175,9 +175,16 @@ class User {
                unset( $this->mSkin );
                $this->mDataLoaded = false;
                $this->mBlockedby = -1; # Unset
-               $this->mTouched = '0'; # Allow any pages to be cached
                $this->setToken(); # Random
                $this->mHash = false;
+
+               if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) {
+                       $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgDBname.'LoggedOut'] );
+               }
+               else {
+                       $this->mTouched = '0'; # Allow any pages to be cached
+               }
+
                wfProfileOut( $fname );
        }
 
@@ -235,9 +242,17 @@ class User {
        /**
         * Get blocking information
         * @access private
+        * @param bool $bFromSlave Specify whether to check slave or master. To improve performance,
+        *  non-critical checks are done against slaves. Check when actually saving should be done against
+        *  master.
+        *
+        * Note that even if $bFromSlave is false, the check is done first against slave, then master.
+        * The logic is that if blocked on slave, we'll assume it's either blocked on master or
+        * just slightly outta sync and soon corrected - safer to block slightly more that less.
+        * And it's cheaper to check slave first, then master if needed, than master always.
         */
        function getBlockedStatus() {
-               global $wgIP, $wgBlockCache, $wgProxyList;
+               global $wgIP, $wgBlockCache, $wgProxyList, $wgEnableSorbs, $bFromSlave;
 
                if ( -1 != $this->mBlockedby ) { return; }
 
@@ -246,15 +261,24 @@ class User {
                # User blocking
                if ( $this->mId ) {
                        $block = new Block();
-                       if ( $block->load( $wgIP , $this->mId ) ) {
+                       $block->forUpdate( $bFromSlave );
+                       if ( $block->load( $wgIP , $this->mId ) ) {
                                $this->mBlockedby = $block->mBy;
                                $this->mBlockreason = $block->mReason;
+                               $this->spreadBlock();
                        }
                }
 
                # IP/range blocking
                if ( !$this->mBlockedby ) {
-                       $block = $wgBlockCache->get( $wgIP );
+                       # Check first against slave, and optionally from master.
+                       $block = $wgBlockCache->get( $wgIP, true );
+                       if ( !$block && !$bFromSlave )
+                               {
+                               # Not blocked: check against master, to make sure.
+                               $wgBlockCache->clearLocal( );
+                               $block = $wgBlockCache->get( $wgIP, false );
+                               }
                        if ( $block !== false ) {
                                $this->mBlockedby = $block->mBy;
                                $this->mBlockreason = $block->mReason;
@@ -264,18 +288,56 @@ class User {
                # Proxy blocking
                if ( !$this->mBlockedby ) {
                        if ( array_key_exists( $wgIP, $wgProxyList ) ) {
+                               $this->mBlockedby = wfMsg( 'proxyblocker' );
                                $this->mBlockreason = wfMsg( 'proxyblockreason' );
-                               $this->mBlockedby = "Proxy blocker";
                        }
                }
+
+               # DNSBL
+               if ( !$this->mBlockedby && $wgEnableSorbs ) {
+                       if ( $this->inSorbsBlacklist( $wgIP ) ) {
+                               $this->mBlockedby = wfMsg( 'sorbs' );
+                               $this->mBlockreason = wfMsg( 'sorbsreason' );
+                       }
+               }
+                       
        }
 
+       function inSorbsBlacklist( $ip ) {
+               $fname = 'User::inSorbsBlacklist';
+               wfProfileIn( $fname );
+               
+               $found = false;
+               $host = '';
+               
+               if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
+                       # Make hostname
+                       for ( $i=4; $i>=1; $i-- ) {
+                               $host .= $m[$i] . '.';
+                       }
+                       $host .= 'http.dnsbl.sorbs.net.';
+
+                       # Send query
+                       $ipList = gethostbynamel( $host );
+                       
+                       if ( $ipList ) {
+                               wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy!\n" );
+                               $found = true;
+                       } else {
+                               wfDebug( "Requested $host, not found.\n" );
+                       }
+               }
+
+               wfProfileOut( $fname );
+               return $found;
+       }
+       
        /**
         * Check if user is blocked
         * @return bool True if blocked, false otherwise
         */
-       function isBlocked() {
-               $this->getBlockedStatus();
+       function isBlocked( $bFromSlave = false ) {
+               $this->getBlockedStatus( $bFromSlave );
                if ( 0 === $this->mBlockedby ) { return false; }
                return true;
        }
@@ -369,7 +431,6 @@ class User {
                                else
                                        wfDebug( "User::loadFromSession() unable to save to memcached\n" );
                        }
-                       $user->spreadBlock();
                        return $user;
                }
                return new User(); # Can't log in from session
@@ -381,7 +442,11 @@ class User {
        function loadFromDatabase() {
                global $wgCommandLineMode, $wgAnonGroupId, $wgLoggedInGroupId;
                $fname = "User::loadFromDatabase";
-               if ( $this->mDataLoaded || $wgCommandLineMode ) {
+               
+               # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress 
+               # loading in a command line script, don't assume all command line scripts need it like this
+               #if ( $this->mDataLoaded || $wgCommandLineMode ) {
+               if ( $this->mDataLoaded ) {
                        return;
                }
 
@@ -411,7 +476,7 @@ class User {
                if ( $s !== false ) {
                        $this->mName = $s->user_name;
                        $this->mEmail = $s->user_email;
-                       $this->mEmailAuthenticationtimestamp = $s->user_emailauthenticationtimestamp;
+                       $this->mEmailAuthenticationtimestamp = wfTimestamp(TS_MW,$s->user_emailauthenticationtimestamp);
                        $this->mRealName = $s->user_real_name;
                        $this->mPassword = $s->user_password;
                        $this->mNewpassword = $s->user_newpassword;
@@ -565,13 +630,16 @@ class User {
 
        # Set the random token (used for persistent authentication)
        function setToken( $token = false ) {
+               global $wgSecretKey, $wgProxyKey, $wgDBname;
                if ( !$token ) {
-                       $this->mToken = '';
-                       # Take random data from PRNG
-                       # This is reasonably secure if the PRNG has been seeded correctly
-                       for ($i = 0; $i<USER_TOKEN_LENGTH / 4; $i++) {
-                               $this->mToken .= sprintf( "%04X", mt_rand( 0, 65535 ) );
+                       if ( $wgSecretKey ) {
+                               $key = $wgSecretKey;
+                       } elseif ( $wgProxyKey ) {
+                               $key = $wgProxyKey;
+                       } else {
+                               $key = microtime();
                        }
+                       $this->mToken = md5( $wgSecretKey . mt_rand( 0, 0x7fffffff ) . $wgDBname . $this->mId );
                } else {
                        $this->mToken = $token;
                }
@@ -654,41 +722,43 @@ class User {
                $this->invalidateCache();
        }
 
+       /**
+        * A more legible check for non-anonymousness.
+        * Returns true if the user is not an anonymous visitor.
+        *
+        * @return bool
+        */
+       function isLoggedIn() {
+               return( $this->getID() != 0 );
+       }
+       
+       /**
+        * A more legible check for anonymousness.
+        * Returns true if the user is an anonymous visitor.
+        *
+        * @return bool
+        */
+       function isAnon() {
+               return !$this->isLoggedIn();
+       }
+       
        /**
         * Check if a user is sysop
         * Die with backtrace. Use User:isAllowed() instead.
         * @deprecated
         */
        function isSysop() {
-       /**
-               $this->loadFromDatabase();
-               if ( 0 == $this->mId ) { return false; }
-
-               return in_array( 'sysop', $this->mRights );
-       */
-       wfDebugDieBacktrace("User::isSysop() is deprecated. Use User::isAllowed() instead");
+               wfDebugDieBacktrace("User::isSysop() is deprecated. Use User::isAllowed() instead");
        }
 
        /** @deprecated */
        function isDeveloper() {
-       /**
-               $this->loadFromDatabase();
-               if ( 0 == $this->mId ) { return false; }
-
-               return in_array( 'developer', $this->mRights );
-       */
-       wfDebugDieBacktrace("User::isDeveloper() is deprecated. Use User::isAllowed() instead");
+               wfDebugDieBacktrace("User::isDeveloper() is deprecated. Use User::isAllowed() instead");
        }
 
        /** @deprecated */
        function isBureaucrat() {
-       /**
-               $this->loadFromDatabase();
-               if ( 0 == $this->mId ) { return false; }
-
-               return in_array( 'bureaucrat', $this->mRights );
-       */
-       wfDebugDieBacktrace("User::isBureaucrat() is deprecated. Use User::isAllowed() instead");
+               wfDebugDieBacktrace("User::isBureaucrat() is deprecated. Use User::isAllowed() instead");
        }
 
        /**
@@ -810,6 +880,9 @@ class User {
         * the next change of the page if it's watched etc.
         */
        function clearNotification( $title ) {
+               $userid = $this->getId();
+               if ($userid==0)
+                       return;
                $dbw =& wfGetDB( DB_MASTER );
                $success = $dbw->update( 'watchlist',
                                array( /* SET */
@@ -907,6 +980,9 @@ class User {
 
                setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
                setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain );
+
+               # Remember when user logged out, to prevent seeing cached pages
+               setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() + 86400, $wgCookiePath, $wgCookieDomain );
        }
 
        /**
@@ -940,7 +1016,7 @@ class User {
                                'user_newpassword' => $this->mNewpassword,
                                'user_real_name' => $this->mRealName,
                                'user_email' => $this->mEmail,
-                               'user_emailauthenticationtimestamp' => $this->mEmailAuthenticationtimestamp,
+                               'user_emailauthenticationtimestamp' => $dbw->timestamp($this->mEmailAuthenticationtimestamp),
                                'user_options' => $this->encodeOptions(),
                                'user_touched' => $dbw->timestamp($this->mTouched),
                                'user_token' => $this->mToken
@@ -1000,7 +1076,7 @@ class User {
                                'user_password' => $this->mPassword,
                                'user_newpassword' => $this->mNewpassword,
                                'user_email' => $this->mEmail,
-                               'user_emailauthenticationtimestamp' => $this->mEmailAuthenticationtimestamp,
+                               'user_emailauthenticationtimestamp' => $dbw->timestamp($this->mEmailAuthenticationtimestamp),
                                'user_real_name' => $this->mRealName,
                                'user_options' => $this->encodeOptions(),
                                'user_token' => $this->mToken
@@ -1025,7 +1101,7 @@ class User {
        }
 
        function spreadBlock() {
-               global $wgIP;
+               global $wgIP;
                # If the (non-anonymous) user is blocked, this function will block any IP address
                # that they successfully log on from.
                $fname = 'User::spreadBlock';
@@ -1079,19 +1155,14 @@ class User {
                // it will always be 0 when this function is called by parsercache.
 
                $confstr =        $this->getOption( 'math' );
-               $confstr .= '!' . $this->getOption( 'highlightbroken' );
                $confstr .= '!' . $this->getOption( 'stubthreshold' );
                $confstr .= '!' . $this->getOption( 'editsection' );
-               $confstr .= '!' . $this->getOption( 'editsectiononrightclick' );
-               $confstr .= '!' . $this->getOption( 'showtoc' );
                $confstr .= '!' . $this->getOption( 'date' );
                $confstr .= '!' . $this->getOption( 'numberheadings' );
                $confstr .= '!' . $this->getOption( 'language' );
                // add in language specific options, if any
                $extra = $wgContLang->getExtraHashOptions();
-               foreach( $extra as $e ) {
-                       $confstr .= '!' . $this->getOption( $e );
-               }
+               $confstr .= $extra;
 
                $this->mHash = $confstr;
                return $confstr ;
@@ -1117,9 +1188,26 @@ class User {
                return wfSetVar( $this->mDataLoaded, $loaded );
        }
 
+       /**
+        * Get this user's personal page title.
+        *
+        * @return Title
+        * @access public
+        */
        function getUserPage() {
                return Title::makeTitle( NS_USER, $this->mName );
        }
+       
+       /**
+        * Get this user's talk page title.
+        *
+        * @return Title
+        * @access public
+        */
+       function getTalkPage() {
+               $title = $this->getUserPage();
+               return $title->getTalkPage();
+       }
 
        /**
         * @static
@@ -1165,13 +1253,52 @@ class User {
                } elseif ( function_exists( 'iconv' ) ) {
                        # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
                        # Check for this with iconv
-/*                     $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
+                       $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252', $password ) );
                        if ( 0 == strcmp( $cp1252hash, $this->mPassword ) ) {
                                return true;
-                       }*/
+                       }
                }
                return false;
        }
+       
+       /**
+        * 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.
+        *
+        * @param mixed $salt - Optional function-specific data for hash.
+        *                      Use a string or an array of strings.
+        * @return string
+        * @access public
+        */
+       function editToken( $salt = '' ) {
+               if( !isset( $_SESSION['wsEditToken'] ) ) {
+                       $token = dechex( mt_rand() ) . dechex( mt_rand() );
+                       $_SESSION['wsEditToken'] = $token;
+               } else {
+                       $token = $_SESSION['wsEditToken'];
+               }
+               if( is_array( $salt ) ) {
+                       $salt = implode( '|', $salt );
+               }
+               return md5( $token . $salt );
+       }
+       
+       /**
+        * Check given value against the token value stored in the session.
+        * A match should confirm that the form was submitted from the
+        * user's own login session, not a form submission from a third-party
+        * site.
+        *
+        * @param string $val - the input value to compare
+        * @param string $salt - Optional function-specific data for hash
+        * @return bool
+        * @access public
+        */
+       function matchEditToken( $val, $salt = '' ) {
+               return ( $val == $this->editToken( $salt ) );
+       }
 }
 
 ?>