X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FUser.php;h=81c45ec1a890677c9836bbaab761a258acac8302;hb=acb150a1054104b8000ff6541749294b29aad1c9;hp=6667a610f6bfe6dc15f3d0486c3c2449c4b9aabd;hpb=a54b50803abb5929617440e6ca7c3e69573d1326;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/User.php b/includes/User.php index 6667a610f6..81c45ec1a8 100644 --- a/includes/User.php +++ b/includes/User.php @@ -21,24 +21,35 @@ define( 'MW_USER_VERSION', 3 ); * @package MediaWiki */ class User { - /**#@+ - * @access private - */ - var $mId, $mName, $mPassword, $mEmail, $mNewtalk; - var $mEmailAuthenticated; - var $mRights, $mOptions; - var $mDataLoaded, $mNewpassword; - var $mSkin; - var $mBlockedby, $mBlockreason; - var $mTouched; - var $mToken; - var $mRealName; - var $mHash; - var $mGroups; - var $mVersion; // serialized version - var $mRegistration; - - /** Construct using User:loadDefaults() */ + /* + * When adding a new private variable, dont forget to add it to __sleep() + */ + /**@{{ + * @private + */ + private $mBlockedby; //!< + private $mBlockreason; //!< + private $mDataLoaded; //!< + private $mEmail; //!< + private $mEmailAuthenticated; //!< + private $mGroups; //!< + private $mHash; //!< + private $mId; //!< + private $mName; //!< + private $mNewpassword; //!< + private $mNewtalk; //!< + private $mOptions; //!< + private $mPassword; //!< + private $mRealName; //!< + private $mRegistration; //!< + private $mRights; //!< + private $mSkin; //!< + private $mToken; //!< + private $mTouched; //!< + private $mVersion; //!< serialized version + /**@}} */ + + /** Constructor using User:loadDefaults() */ function User() { $this->loadDefaults(); $this->mVersion = MW_USER_VERSION; @@ -102,13 +113,31 @@ class User { /** * Serialze sleep function, for better cache efficiency and avoidance of - * silly "incomplete type" errors when skins are cached + * silly "incomplete type" errors when skins are cached. The array should + * contain names of private variables (see at top of User.php). */ function __sleep() { - return array( 'mId', 'mName', 'mPassword', 'mEmail', 'mNewtalk', - 'mEmailAuthenticated', 'mRights', 'mOptions', 'mDataLoaded', - 'mNewpassword', 'mBlockedby', 'mBlockreason', 'mTouched', - 'mToken', 'mRealName', 'mHash', 'mGroups', 'mRegistration' ); + return array( +'mBlockedby', +'mBlockreason', +'mDataLoaded', +'mEmail', +'mEmailAuthenticated', +'mGroups', +'mHash', +'mId', +'mName', +'mNewpassword', +'mNewtalk', +'mOptions', +'mPassword', +'mRealName', +'mRegistration', +'mRights', +'mToken', +'mTouched', +'mVersion', +); } /** @@ -202,7 +231,7 @@ class User { || strlen( $name ) > $wgMaxNameChars || $name != $wgContLang->ucfirst( $name ) ) return false; - + // Ensure that the name can't be misresolved as a different title, // such as with extra namespace keys at the start. $parsed = Title::newFromText( $name ); @@ -210,8 +239,22 @@ class User { || $parsed->getNamespace() || strcmp( $name, $parsed->getPrefixedText() ) ) return false; - else - return true; + + // Check an additional blacklist of troublemaker characters. + // Should these be merged into the title char list? + $unicodeBlacklist = '/[' . + '\x{0080}-\x{009f}' . # iso-8859-1 control chars + '\x{00a0}' . # non-breaking space + '\x{2000}-\x{200f}' . # various whitespace + '\x{2028}-\x{202f}' . # breaks and control chars + '\x{3000}' . # ideographic space + '\x{e000}-\x{f8ff}' . # private use + ']/u'; + if( preg_match( $unicodeBlacklist, $name ) ) { + return false; + } + + return true; } /** @@ -293,7 +336,7 @@ class User { $fname = 'User::loadDefaults' . $n; wfProfileIn( $fname ); - global $wgContLang, $wgDBname; + global $wgCookiePrefix; global $wgNamespacesToBeSearchedDefault; $this->mId = 0; @@ -315,15 +358,15 @@ class User { $this->setToken(); # Random $this->mHash = false; - if ( isset( $_COOKIE[$wgDBname.'LoggedOut'] ) ) { - $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgDBname.'LoggedOut'] ); + if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) { + $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgCookiePrefix.'LoggedOut'] ); } else { $this->mTouched = '0'; # Allow any pages to be cached } $this->mRegistration = wfTimestamp( TS_MW ); - + wfProfileOut( $fname ); } @@ -333,7 +376,7 @@ class User { * * @return array * @static - * @access private + * @private */ function getDefaultOptions() { /** @@ -358,7 +401,7 @@ class User { * @param string $opt * @return string * @static - * @access public + * @public */ function getDefaultOption( $opt ) { $defOpts = User::getDefaultOptions(); @@ -371,7 +414,7 @@ class User { /** * Get blocking information - * @access private + * @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. @@ -406,7 +449,8 @@ class User { } # Proxy blocking - if ( !$this->isSysop() && !in_array( $ip, $wgProxyWhitelist ) ) { + # FIXME ? proxyunbannable is to deprecate the old isSysop() + if ( !$this->isAllowed('proxyunbannable') && !in_array( $ip, $wgProxyWhitelist ) ) { # Local list if ( wfIsLocallyBlockedProxy( $ip ) ) { @@ -478,7 +522,7 @@ class User { * last-hit counters will be shared across wikis. * * @return bool true if a rate limiter was tripped - * @access public + * @public */ function pingLimiter( $action='edit' ) { global $wgRateLimits; @@ -597,8 +641,9 @@ class User { /** * Initialise php session + * @static */ - function SetupSession() { + static function SetupSession() { global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain; if( $wgSessionsInMemcached ) { require_once( 'MemcachedSessions.php' ); @@ -616,8 +661,8 @@ class User { * Create a new user object using data from session * @static */ - function loadFromSession() { - global $wgMemc, $wgDBname; + static function loadFromSession() { + global $wgMemc, $wgDBname, $wgCookiePrefix; if ( isset( $_SESSION['wsUserID'] ) ) { if ( 0 != $_SESSION['wsUserID'] ) { @@ -625,16 +670,16 @@ class User { } else { return new User(); } - } else if ( isset( $_COOKIE["{$wgDBname}UserID"] ) ) { - $sId = intval( $_COOKIE["{$wgDBname}UserID"] ); + } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserID"] ) ) { + $sId = intval( $_COOKIE["{$wgCookiePrefix}UserID"] ); $_SESSION['wsUserID'] = $sId; } else { return new User(); } if ( isset( $_SESSION['wsUserName'] ) ) { $sName = $_SESSION['wsUserName']; - } else if ( isset( $_COOKIE["{$wgDBname}UserName"] ) ) { - $sName = $_COOKIE["{$wgDBname}UserName"]; + } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserName"] ) ) { + $sName = $_COOKIE["{$wgCookiePrefix}UserName"]; $_SESSION['wsUserName'] = $sName; } else { return new User(); @@ -654,11 +699,11 @@ class User { } else { wfDebug( "User::loadFromSession() got from cache!\n" ); } - + if ( isset( $_SESSION['wsToken'] ) ) { $passwordCorrect = $_SESSION['wsToken'] == $user->mToken; - } else if ( isset( $_COOKIE["{$wgDBname}Token"] ) ) { - $passwordCorrect = $user->mToken == $_COOKIE["{$wgDBname}Token"]; + } else if ( isset( $_COOKIE["{$wgCookiePrefix}Token"] ) ) { + $passwordCorrect = $user->mToken == $_COOKIE["{$wgCookiePrefix}Token"]; } else { return new User(); # Can't log in from session } @@ -726,13 +771,13 @@ class User { $this->mGroups[] = $row->ug_group; } $implicitGroups = array( '*', 'user' ); - + global $wgAutoConfirmAge; $accountAge = time() - wfTimestampOrNull( TS_UNIX, $this->mRegistration ); if( $accountAge >= $wgAutoConfirmAge ) { $implicitGroups[] = 'autoconfirmed'; } - + $effectiveGroups = array_merge( $implicitGroups, $this->mGroups ); $this->mRights = $this->getGroupPermissions( $effectiveGroups ); } @@ -763,7 +808,7 @@ class User { /** * Return the title dbkey form of the name, for eg user pages. * @return string - * @access public + * @public */ function getTitleKey() { return str_replace( ' ', '_', $this->getName() ); @@ -796,6 +841,23 @@ class User { return (bool)$this->mNewtalk; } + /** + * Return the talk page(s) this user has new messages on. + */ + function getNewMessageLinks() { + global $wgDBname; + $talks = array(); + if (!wfRunHooks('UserRetrieveNewTalks', array(&$this, &$talks))) + return $talks; + + if (!$this->getNewtalk()) + return array(); + $up = $this->getUserPage(); + $utp = $up->getTalkPage(); + return array(array("wiki" => $wgDBname, "link" => $utp->getLocalURL())); + } + + /** * Perform a user_newtalk check on current slaves; if the memcached data * is funky we don't want newtalk state to get stuck on save, as that's @@ -804,7 +866,7 @@ class User { * @param string $field * @param mixed $id * @return bool - * @access private + * @private */ function checkNewtalk( $field, $id ) { $fname = 'User::checkNewtalk'; @@ -813,12 +875,12 @@ class User { array( $field => $id ), $fname ); return $ok !== false; } - + /** - * Add or update the + * Add or update the * @param string $field * @param mixed $id - * @access private + * @private */ function updateNewtalk( $field, $id ) { $fname = 'User::updateNewtalk'; @@ -834,12 +896,12 @@ class User { wfDebug( "$fname: set on ($field, $id)\n" ); return true; } - + /** * Clear the new messages flag for the given user * @param string $field * @param mixed $id - * @access private + * @private */ function deleteNewtalk( $field, $id ) { $fname = 'User::deleteNewtalk'; @@ -854,7 +916,7 @@ class User { wfDebug( "$fname: killed on ($field, $id)\n" ); return true; } - + /** * Update the 'You have new messages!' status. * @param bool $val @@ -863,12 +925,12 @@ class User { if( wfReadOnly() ) { return; } - + $this->loadFromDatabase(); $this->mNewtalk = $val; $fname = 'User::setNewtalk'; - + if( $this->isAnon() ) { $field = 'user_ip'; $id = $this->getName(); @@ -876,19 +938,19 @@ class User { $field = 'user_id'; $id = $this->getId(); } - + if( $val ) { $changed = $this->updateNewtalk( $field, $id ); } else { $changed = $this->deleteNewtalk( $field, $id ); } - + if( $changed ) { if( $this->isAnon() ) { // Anons have a separate memcached space, since // user records aren't kept for them. global $wgDBname, $wgMemc; - $key = "$wgDBname:newtalk:ip:$value"; + $key = "$wgDBname:newtalk:ip:$val"; $wgMemc->set( $key, $val ? 1 : 0 ); } else { if( $val ) { @@ -986,6 +1048,10 @@ class User { $this->mRealName = $str; } + /** + * @param string $oname The option to check + * @return string + */ function getOption( $oname ) { $this->loadFromDatabase(); if ( array_key_exists( $oname, $this->mOptions ) ) { @@ -995,6 +1061,14 @@ class User { } } + /** + * @param string $oname The option to check + * @return bool False if the option is not selected, true if it is + */ + function getBoolOption( $oname ) { + return (bool)$this->getOption( $oname ); + } + function setOption( $oname, $val ) { $this->loadFromDatabase(); if ( $oname == 'skin' ) { @@ -1099,21 +1173,30 @@ class User { } /** - * Check if a user is sysop + * Deprecated in 1.6, die in 1.7, to be removed in 1.8 * @deprecated */ function isSysop() { - return $this->isAllowed( 'protect' ); + wfDebugDieBacktrace( "Call to deprecated (v1.7) User::isSysop() method\n" ); + #return $this->isAllowed( 'protect' ); } - /** @deprecated */ + /** + * Deprecated in 1.6, die in 1.7, to be removed in 1.8 + * @deprecated + */ function isDeveloper() { - return $this->isAllowed( 'siteadmin' ); + wfDebugDieBacktrace( "Call to deprecated (v1.7) User::isDeveloper() method\n" ); + #return $this->isAllowed( 'siteadmin' ); } - /** @deprecated */ + /** + * Deprecated in 1.6, die in 1.7, to be removed in 1.8 + * @deprecated + */ function isBureaucrat() { - return $this->isAllowed( 'makesysop' ); + wfDebugDieBacktrace( "Call to deprecated (v1.7) User::isBureaucrat() method\n" ); + #return $this->isAllowed( 'makesysop' ); } /** @@ -1149,13 +1232,10 @@ class User { $fname = 'User::getSkin'; wfProfileIn( $fname ); - # get all skin names available - $skinNames = Skin::getSkinNames(); - # get the user skin $userSkin = $this->getOption( 'skin' ); $userSkin = $wgRequest->getVal('useskin', $userSkin); - + $this->mSkin =& Skin::newFromKey( $userSkin ); wfProfileOut( $fname ); } @@ -1201,11 +1281,14 @@ class User { function clearNotification( &$title ) { global $wgUser, $wgUseEnotif; + if ($title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) { + if (!wfRunHooks('UserClearNewTalkNotification', array(&$this))) + return; $this->setNewtalk( false ); } - + if( !$wgUseEnotif ) { return; } @@ -1253,7 +1336,7 @@ class User { * the next change of any watched page. * * @param int $currentUser user ID number - * @access public + * @public */ function clearAllNotifications( $currentUser ) { global $wgUseEnotif; @@ -1278,7 +1361,7 @@ class User { } /** - * @access private + * @private * @return string Encoding options */ function encodeOptions() { @@ -1291,7 +1374,7 @@ class User { } /** - * @access private + * @private */ function decodeOptions( $str ) { $a = explode( "\n", $str ); @@ -1303,22 +1386,22 @@ class User { } function setCookies() { - global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgDBname; + global $wgCookieExpiration, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix; if ( 0 == $this->mId ) return; $this->loadFromDatabase(); $exp = time() + $wgCookieExpiration; $_SESSION['wsUserID'] = $this->mId; - setcookie( $wgDBname.'UserID', $this->mId, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'UserID', $this->mId, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); $_SESSION['wsUserName'] = $this->getName(); - setcookie( $wgDBname.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'UserName', $this->getName(), $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); $_SESSION['wsToken'] = $this->mToken; if ( 1 == $this->getOption( 'rememberpassword' ) ) { - setcookie( $wgDBname.'Token', $this->mToken, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'Token', $this->mToken, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); } else { - setcookie( $wgDBname.'Token', '', time() - 3600 ); + setcookie( $wgCookiePrefix.'Token', '', time() - 3600 ); } } @@ -1327,24 +1410,24 @@ class User { * It will clean the session cookie */ function logout() { - global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgDBname; + global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix; $this->loadDefaults(); $this->setLoaded( true ); $_SESSION['wsUserID'] = 0; - setcookie( $wgDBname.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); - setcookie( $wgDBname.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'UserID', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'Token', '', time() - 3600, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); # Remember when user logged out, to prevent seeing cached pages - setcookie( $wgDBname.'LoggedOut', wfTimestampNow(), time() + 86400, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); + setcookie( $wgCookiePrefix.'LoggedOut', wfTimestampNow(), time() + 86400, $wgCookiePath, $wgCookieDomain, $wgCookieSecure ); } /** * Save object settings into database */ function saveSettings() { - global $wgMemc, $wgDBname, $wgUseEnotif; + global $wgMemc, $wgDBname; $fname = 'User::saveSettings'; if ( wfReadOnly() ) { return; } @@ -1494,7 +1577,7 @@ class User { // add in language specific options, if any $extra = $wgContLang->getExtraHashOptions(); $confstr .= $extra; - + // Give a chance for extensions to modify the hash, if they have // extra options or other effects on the parser cache. wfRunHooks( 'PageRenderingHash', array( &$confstr ) ); @@ -1519,7 +1602,7 @@ class User { * Get this user's personal page title. * * @return Title - * @access public + * @public */ function getUserPage() { return Title::makeTitle( NS_USER, $this->getName() ); @@ -1529,7 +1612,7 @@ class User { * Get this user's talk page title. * * @return Title - * @access public + * @public */ function getTalkPage() { $title = $this->getUserPage(); @@ -1608,7 +1691,7 @@ class User { * @param mixed $salt - Optional function-specific data for hash. * Use a string or an array of strings. * @return string - * @access public + * @public */ function editToken( $salt = '' ) { if( !isset( $_SESSION['wsEditToken'] ) ) { @@ -1642,27 +1725,15 @@ class User { * @param string $val - the input value to compare * @param string $salt - Optional function-specific data for hash * @return bool - * @access public + * @public */ function matchEditToken( $val, $salt = '' ) { global $wgMemc; - -/* - if ( !isset( $_SESSION['wsEditToken'] ) ) { - $logfile = '/home/wikipedia/logs/session_debug/session.log'; - $mckey = memsess_key( session_id() ); - $uname = @posix_uname(); - $msg = "wsEditToken not set!\n" . - 'apache server=' . $uname['nodename'] . "\n" . - 'session_id = ' . session_id() . "\n" . - '$_SESSION=' . var_export( $_SESSION, true ) . "\n" . - '$_COOKIE=' . var_export( $_COOKIE, true ) . "\n" . - "mc get($mckey) = " . var_export( $wgMemc->get( $mckey ), true ) . "\n\n\n"; - - @error_log( $msg, 3, $logfile ); + $sessionToken = $this->editToken( $salt ); + if ( $val != $sessionToken ) { + wfDebug( "User::matchEditToken: broken session data\n" ); } -*/ - return ( $val == $this->editToken( $salt ) ); + return $val == $sessionToken; } /** @@ -1714,7 +1785,7 @@ class User { * A hash (unsalted since it's used as a key) is stored. * @param &$expiration mixed output: accepts the expiration time * @return string - * @access private + * @private */ function confirmationToken( &$expiration ) { $fname = 'User::confirmationToken'; @@ -1741,7 +1812,7 @@ class User { * the URL the user can use to confirm. * @param &$expiration mixed output: accepts the expiration time * @return string - * @access private + * @private */ function confirmationTokenUrl( &$expiration ) { $token = $this->confirmationToken( $expiration ); @@ -1790,13 +1861,18 @@ class User { function isEmailConfirmed() { global $wgEmailAuthentication; $this->loadFromDatabase(); - if( $this->isAnon() ) - return false; - if( !$this->isValidEmailAddr( $this->mEmail ) ) - return false; - if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() ) - return false; - return true; + $confirmed = true; + if( wfRunHooks( 'EmailConfirmed', array( &$this, &$confirmed ) ) ) { + if( $this->isAnon() ) + return false; + if( !$this->isValidEmailAddr( $this->mEmail ) ) + return false; + if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() ) + return false; + return true; + } else { + return $confirmed; + } } /** @@ -1818,11 +1894,26 @@ class User { /** * @param string $group key name - * @return string localized descriptive name, if provided + * @return string localized descriptive name for group, if provided * @static */ function getGroupName( $group ) { - $key = "group-$group-name"; + $key = "group-$group"; + $name = wfMsg( $key ); + if( $name == '' || $name == "<$key>" ) { + return $group; + } else { + return $name; + } + } + + /** + * @param string $group key name + * @return string localized descriptive name for member of a group, if provided + * @static + */ + function getGroupMember( $group ) { + $key = "group-$group-member"; $name = wfMsg( $key ); if( $name == '' || $name == "<$key>" ) { return $group; @@ -1831,6 +1922,7 @@ class User { } } + /** * Return the set of defined explicit groups. * The * and 'user' groups are not included. @@ -1844,6 +1936,23 @@ class User { array( '*', 'user', 'autoconfirmed' ) ); } + /** + * Get the title of a page describing a particular group + * + * @param $group Name of the group + * @return mixed + */ + function getGroupPage( $group ) { + $page = wfMsgForContent( 'grouppage-' . $group ); + if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) { + $title = Title::newFromText( $page ); + if( is_object( $title ) ) + return $title; + } + return false; + } + + } ?>