00001 <?php
00011 define( 'USER_TOKEN_LENGTH', 32 );
00012
00017 define( 'MW_USER_VERSION', 8 );
00018
00023 define( 'EDIT_TOKEN_SUFFIX', '+\\' );
00024
00029 class PasswordError extends MWException {
00030
00031 }
00032
00043 class User {
00044
00052 public static $mToggles = array(
00053 'highlightbroken',
00054 'justify',
00055 'hideminor',
00056 'extendwatchlist',
00057 'usenewrc',
00058 'numberheadings',
00059 'showtoolbar',
00060 'editondblclick',
00061 'editsection',
00062 'editsectiononrightclick',
00063 'showtoc',
00064 'rememberpassword',
00065 'editwidth',
00066 'watchcreations',
00067 'watchdefault',
00068 'watchmoves',
00069 'watchdeletion',
00070 'minordefault',
00071 'previewontop',
00072 'previewonfirst',
00073 'nocache',
00074 'enotifwatchlistpages',
00075 'enotifusertalkpages',
00076 'enotifminoredits',
00077 'enotifrevealaddr',
00078 'shownumberswatching',
00079 'fancysig',
00080 'externaleditor',
00081 'externaldiff',
00082 'showjumplinks',
00083 'uselivepreview',
00084 'forceeditsummary',
00085 'watchlisthideminor',
00086 'watchlisthidebots',
00087 'watchlisthideown',
00088 'watchlisthideanons',
00089 'watchlisthideliu',
00090 'ccmeonemails',
00091 'diffonly',
00092 'showhiddencats',
00093 'noconvertlink',
00094 'norollbackdiff',
00095 );
00096
00103 static $mCacheVars = array(
00104
00105 'mId',
00106 'mName',
00107 'mRealName',
00108 'mPassword',
00109 'mNewpassword',
00110 'mNewpassTime',
00111 'mEmail',
00112 'mTouched',
00113 'mToken',
00114 'mEmailAuthenticated',
00115 'mEmailToken',
00116 'mEmailTokenExpires',
00117 'mRegistration',
00118 'mEditCount',
00119
00120 'mGroups',
00121
00122 'mOptionOverrides',
00123 );
00124
00131 static $mCoreRights = array(
00132 'apihighlimits',
00133 'autoconfirmed',
00134 'autopatrol',
00135 'bigdelete',
00136 'block',
00137 'blockemail',
00138 'bot',
00139 'browsearchive',
00140 'createaccount',
00141 'createpage',
00142 'createtalk',
00143 'delete',
00144 'deletedhistory',
00145 'deletedtext',
00146 'deleterevision',
00147 'edit',
00148 'editinterface',
00149 'editusercssjs',
00150 'hideuser',
00151 'import',
00152 'importupload',
00153 'ipblock-exempt',
00154 'markbotedits',
00155 'minoredit',
00156 'move',
00157 'movefile',
00158 'move-rootuserpages',
00159 'move-subpages',
00160 'nominornewtalk',
00161 'noratelimit',
00162 'override-export-depth',
00163 'patrol',
00164 'protect',
00165 'proxyunbannable',
00166 'purge',
00167 'read',
00168 'reupload',
00169 'reupload-shared',
00170 'rollback',
00171 'sendemail',
00172 'siteadmin',
00173 'suppressionlog',
00174 'suppressredirect',
00175 'suppressrevision',
00176 'trackback',
00177 'undelete',
00178 'unwatchedpages',
00179 'upload',
00180 'upload_by_url',
00181 'userrights',
00182 'userrights-interwiki',
00183 'writeapi',
00184 );
00188 static $mAllRights = false;
00189
00192 var $mId, $mName, $mRealName, $mPassword, $mNewpassword, $mNewpassTime,
00193 $mEmail, $mTouched, $mToken, $mEmailAuthenticated,
00194 $mEmailToken, $mEmailTokenExpires, $mRegistration, $mGroups, $mOptionOverrides;
00196
00200 var $mDataLoaded, $mAuthLoaded, $mOptionsLoaded;
00201
00211 var $mFrom;
00212
00215 var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mSkin, $mRights,
00216 $mBlockreason, $mBlock, $mEffectiveGroups, $mBlockedGlobally,
00217 $mLocked, $mHideName, $mOptions;
00219
00220 static $idCacheByName = array();
00221
00232 function User() {
00233 $this->clearInstanceCache( 'defaults' );
00234 }
00235
00239 function load() {
00240 if ( $this->mDataLoaded ) {
00241 return;
00242 }
00243 wfProfileIn( __METHOD__ );
00244
00245 # Set it now to avoid infinite recursion in accessors
00246 $this->mDataLoaded = true;
00247
00248 switch ( $this->mFrom ) {
00249 case 'defaults':
00250 $this->loadDefaults();
00251 break;
00252 case 'name':
00253 $this->mId = self::idFromName( $this->mName );
00254 if ( !$this->mId ) {
00255 # Nonexistent user placeholder object
00256 $this->loadDefaults( $this->mName );
00257 } else {
00258 $this->loadFromId();
00259 }
00260 break;
00261 case 'id':
00262 $this->loadFromId();
00263 break;
00264 case 'session':
00265 $this->loadFromSession();
00266 wfRunHooks( 'UserLoadAfterLoadFromSession', array( $this ) );
00267 break;
00268 default:
00269 throw new MWException( "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
00270 }
00271 wfProfileOut( __METHOD__ );
00272 }
00273
00279 function loadFromId() {
00280 global $wgMemc;
00281 if ( $this->mId == 0 ) {
00282 $this->loadDefaults();
00283 return false;
00284 }
00285
00286 # Try cache
00287 $key = wfMemcKey( 'user', 'id', $this->mId );
00288 $data = $wgMemc->get( $key );
00289 if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
00290 # Object is expired, load from DB
00291 $data = false;
00292 }
00293
00294 if ( !$data ) {
00295 wfDebug( "Cache miss for user {$this->mId}\n" );
00296 # Load from DB
00297 if ( !$this->loadFromDatabase() ) {
00298 # Can't load from ID, user is anonymous
00299 return false;
00300 }
00301 $this->saveToCache();
00302 } else {
00303 wfDebug( "Got user {$this->mId} from cache\n" );
00304 # Restore from cache
00305 foreach ( self::$mCacheVars as $name ) {
00306 $this->$name = $data[$name];
00307 }
00308 }
00309 return true;
00310 }
00311
00315 function saveToCache() {
00316 $this->load();
00317 $this->loadGroups();
00318 $this->loadOptions();
00319 if ( $this->isAnon() ) {
00320
00321 return;
00322 }
00323 $data = array();
00324 foreach ( self::$mCacheVars as $name ) {
00325 $data[$name] = $this->$name;
00326 }
00327 $data['mVersion'] = MW_USER_VERSION;
00328 $key = wfMemcKey( 'user', 'id', $this->mId );
00329 global $wgMemc;
00330 $wgMemc->set( $key, $data );
00331 }
00332
00333
00336
00353 static function newFromName( $name, $validate = 'valid' ) {
00354 if ( $validate === true ) {
00355 $validate = 'valid';
00356 }
00357 $name = self::getCanonicalName( $name, $validate );
00358 if ( $name === false ) {
00359 return false;
00360 } else {
00361 # Create unloaded user object
00362 $u = new User;
00363 $u->mName = $name;
00364 $u->mFrom = 'name';
00365 return $u;
00366 }
00367 }
00368
00375 static function newFromId( $id ) {
00376 $u = new User;
00377 $u->mId = $id;
00378 $u->mFrom = 'id';
00379 return $u;
00380 }
00381
00392 static function newFromConfirmationCode( $code ) {
00393 $dbr = wfGetDB( DB_SLAVE );
00394 $id = $dbr->selectField( 'user', 'user_id', array(
00395 'user_email_token' => md5( $code ),
00396 'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
00397 ) );
00398 if( $id !== false ) {
00399 return User::newFromId( $id );
00400 } else {
00401 return null;
00402 }
00403 }
00404
00411 static function newFromSession() {
00412 $user = new User;
00413 $user->mFrom = 'session';
00414 return $user;
00415 }
00416
00423 static function newFromRow( $row ) {
00424 $user = new User;
00425 $user->loadFromRow( $row );
00426 return $user;
00427 }
00428
00430
00431
00437 static function whoIs( $id ) {
00438 $dbr = wfGetDB( DB_SLAVE );
00439 return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), __METHOD__ );
00440 }
00441
00448 static function whoIsReal( $id ) {
00449 $dbr = wfGetDB( DB_SLAVE );
00450 return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), __METHOD__ );
00451 }
00452
00458 static function idFromName( $name ) {
00459 $nt = Title::makeTitleSafe( NS_USER, $name );
00460 if( is_null( $nt ) ) {
00461 # Illegal name
00462 return null;
00463 }
00464
00465 if ( isset( self::$idCacheByName[$name] ) ) {
00466 return self::$idCacheByName[$name];
00467 }
00468
00469 $dbr = wfGetDB( DB_SLAVE );
00470 $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), __METHOD__ );
00471
00472 if ( $s === false ) {
00473 $result = null;
00474 } else {
00475 $result = $s->user_id;
00476 }
00477
00478 self::$idCacheByName[$name] = $result;
00479
00480 if ( count( self::$idCacheByName ) > 1000 ) {
00481 self::$idCacheByName = array();
00482 }
00483
00484 return $result;
00485 }
00486
00503 static function isIP( $name ) {
00504 return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name) || IP::isIPv6($name);
00505 }
00506
00518 static function isValidUserName( $name ) {
00519 global $wgContLang, $wgMaxNameChars;
00520
00521 if ( $name == ''
00522 || User::isIP( $name )
00523 || strpos( $name, '/' ) !== false
00524 || strlen( $name ) > $wgMaxNameChars
00525 || $name != $wgContLang->ucfirst( $name ) ) {
00526 wfDebugLog( 'username', __METHOD__ .
00527 ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
00528 return false;
00529 }
00530
00531
00532
00533 $parsed = Title::newFromText( $name );
00534 if( is_null( $parsed )
00535 || $parsed->getNamespace()
00536 || strcmp( $name, $parsed->getPrefixedText() ) ) {
00537 wfDebugLog( 'username', __METHOD__ .
00538 ": '$name' invalid due to ambiguous prefixes" );
00539 return false;
00540 }
00541
00542
00543
00544 $unicodeBlacklist = '/[' .
00545 '\x{0080}-\x{009f}' . # iso-8859-1 control chars
00546 '\x{00a0}' . # non-breaking space
00547 '\x{2000}-\x{200f}' . # various whitespace
00548 '\x{2028}-\x{202f}' . # breaks and control chars
00549 '\x{3000}' . # ideographic space
00550 '\x{e000}-\x{f8ff}' . # private use
00551 ']/u';
00552 if( preg_match( $unicodeBlacklist, $name ) ) {
00553 wfDebugLog( 'username', __METHOD__ .
00554 ": '$name' invalid due to blacklisted characters" );
00555 return false;
00556 }
00557
00558 return true;
00559 }
00560
00572 static function isUsableName( $name ) {
00573 global $wgReservedUsernames;
00574
00575 if ( !self::isValidUserName( $name ) ) {
00576 return false;
00577 }
00578
00579 static $reservedUsernames = false;
00580 if ( !$reservedUsernames ) {
00581 $reservedUsernames = $wgReservedUsernames;
00582 wfRunHooks( 'UserGetReservedNames', array( &$reservedUsernames ) );
00583 }
00584
00585
00586 foreach ( $reservedUsernames as $reserved ) {
00587 if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
00588 $reserved = wfMsgForContent( substr( $reserved, 4 ) );
00589 }
00590 if ( $reserved == $name ) {
00591 return false;
00592 }
00593 }
00594 return true;
00595 }
00596
00610 static function isCreatableName( $name ) {
00611 global $wgInvalidUsernameCharacters;
00612 return
00613 self::isUsableName( $name ) &&
00614
00615
00616 !preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name );
00617 }
00618
00625 function isValidPassword( $password ) {
00626
00627 return $this->getPasswordValidity( $password ) === true;
00628 }
00629
00636 function getPasswordValidity( $password ) {
00637 global $wgMinimalPasswordLength, $wgContLang;
00638
00639 $result = false;
00640
00641 if( !wfRunHooks( 'isValidPassword', array( $password, &$result, $this ) ) )
00642 return $result;
00643
00644 if ( $result === false ) {
00645 if( strlen( $password ) < $wgMinimalPasswordLength ) {
00646 return 'passwordtooshort';
00647 } elseif ( $wgContLang->lc( $password ) == $wgContLang->lc( $this->mName ) ) {
00648 return 'password-name-match';
00649 } else {
00650
00651
00652
00653
00654 return true;
00655 }
00656 } elseif( $result === true ) {
00657 return true;
00658 } else {
00659 return $result;
00660 }
00661 }
00662
00675 public static function isValidEmailAddr( $addr ) {
00676 $result = null;
00677 if( !wfRunHooks( 'isValidEmailAddr', array( $addr, &$result ) ) ) {
00678 return $result;
00679 }
00680
00681 return strpos( $addr, '@' ) !== false;
00682 }
00683
00694 static function getCanonicalName( $name, $validate = 'valid' ) {
00695 # Force usernames to capital
00696 global $wgContLang;
00697 $name = $wgContLang->ucfirst( $name );
00698
00699 # Reject names containing '#'; these will be cleaned up
00700 # with title normalisation, but then it's too late to
00701 # check elsewhere
00702 if( strpos( $name, '#' ) !== false )
00703 return false;
00704
00705 # Clean up name according to title rules
00706 $t = ( $validate === 'valid' ) ?
00707 Title::newFromText( $name ) : Title::makeTitle( NS_USER, $name );
00708 # Check for invalid titles
00709 if( is_null( $t ) ) {
00710 return false;
00711 }
00712
00713 # Reject various classes of invalid names
00714 $name = $t->getText();
00715 global $wgAuth;
00716 $name = $wgAuth->getCanonicalName( $t->getText() );
00717
00718 switch ( $validate ) {
00719 case false:
00720 break;
00721 case 'valid':
00722 if ( !User::isValidUserName( $name ) ) {
00723 $name = false;
00724 }
00725 break;
00726 case 'usable':
00727 if ( !User::isUsableName( $name ) ) {
00728 $name = false;
00729 }
00730 break;
00731 case 'creatable':
00732 if ( !User::isCreatableName( $name ) ) {
00733 $name = false;
00734 }
00735 break;
00736 default:
00737 throw new MWException( 'Invalid parameter value for $validate in ' . __METHOD__ );
00738 }
00739 return $name;
00740 }
00741
00749 static function edits( $uid ) {
00750 wfProfileIn( __METHOD__ );
00751 $dbr = wfGetDB( DB_SLAVE );
00752
00753 $field = $dbr->selectField(
00754 'user', 'user_editcount',
00755 array( 'user_id' => $uid ),
00756 __METHOD__
00757 );
00758
00759 if( $field === null ) {
00760 $dbw = wfGetDB( DB_MASTER );
00761 $count = $dbr->selectField(
00762 'revision', 'count(*)',
00763 array( 'rev_user' => $uid ),
00764 __METHOD__
00765 );
00766 $dbw->update(
00767 'user',
00768 array( 'user_editcount' => $count ),
00769 array( 'user_id' => $uid ),
00770 __METHOD__
00771 );
00772 } else {
00773 $count = $field;
00774 }
00775 wfProfileOut( __METHOD__ );
00776 return $count;
00777 }
00778
00785 static function randomPassword() {
00786 global $wgMinimalPasswordLength;
00787 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
00788 $l = strlen( $pwchars ) - 1;
00789
00790 $pwlength = max( 7, $wgMinimalPasswordLength );
00791 $digit = mt_rand( 0, $pwlength - 1 );
00792 $np = '';
00793 for ( $i = 0; $i < $pwlength; $i++ ) {
00794 $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars{ mt_rand( 0, $l ) };
00795 }
00796 return $np;
00797 }
00798
00806 function loadDefaults( $name = false ) {
00807 wfProfileIn( __METHOD__ );
00808
00809 global $wgCookiePrefix;
00810
00811 $this->mId = 0;
00812 $this->mName = $name;
00813 $this->mRealName = '';
00814 $this->mPassword = $this->mNewpassword = '';
00815 $this->mNewpassTime = null;
00816 $this->mEmail = '';
00817 $this->mOptionOverrides = null;
00818 $this->mOptionsLoaded = false;
00819
00820 if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) {
00821 $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgCookiePrefix.'LoggedOut'] );
00822 } else {
00823 $this->mTouched = '0'; # Allow any pages to be cached
00824 }
00825
00826 $this->setToken(); # Random
00827 $this->mEmailAuthenticated = null;
00828 $this->mEmailToken = '';
00829 $this->mEmailTokenExpires = null;
00830 $this->mRegistration = wfTimestamp( TS_MW );
00831 $this->mGroups = array();
00832
00833 wfRunHooks( 'UserLoadDefaults', array( $this, $name ) );
00834
00835 wfProfileOut( __METHOD__ );
00836 }
00837
00841 function SetupSession() {
00842 wfDeprecated( __METHOD__ );
00843 wfSetupSession();
00844 }
00845
00851 private function loadFromSession() {
00852 global $wgMemc, $wgCookiePrefix, $wgExternalAuthType, $wgAutocreatePolicy;
00853
00854 $result = null;
00855 wfRunHooks( 'UserLoadFromSession', array( $this, &$result ) );
00856 if ( $result !== null ) {
00857 return $result;
00858 }
00859
00860 if ( $wgExternalAuthType && $wgAutocreatePolicy == 'view' ) {
00861 $extUser = ExternalUser::newFromCookie();
00862 if ( $extUser ) {
00863 # TODO: Automatically create the user here (or probably a bit
00864 # lower down, in fact)
00865 }
00866 }
00867
00868 if ( isset( $_COOKIE["{$wgCookiePrefix}UserID"] ) ) {
00869 $sId = intval( $_COOKIE["{$wgCookiePrefix}UserID"] );
00870 if( isset( $_SESSION['wsUserID'] ) && $sId != $_SESSION['wsUserID'] ) {
00871 $this->loadDefaults();
00872 wfDebugLog( 'loginSessions', "Session user ID ({$_SESSION['wsUserID']}) and
00873 cookie user ID ($sId) don't match!" );
00874 return false;
00875 }
00876 $_SESSION['wsUserID'] = $sId;
00877 } else if ( isset( $_SESSION['wsUserID'] ) ) {
00878 if ( $_SESSION['wsUserID'] != 0 ) {
00879 $sId = $_SESSION['wsUserID'];
00880 } else {
00881 $this->loadDefaults();
00882 return false;
00883 }
00884 } else {
00885 $this->loadDefaults();
00886 return false;
00887 }
00888
00889 if ( isset( $_SESSION['wsUserName'] ) ) {
00890 $sName = $_SESSION['wsUserName'];
00891 } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserName"] ) ) {
00892 $sName = $_COOKIE["{$wgCookiePrefix}UserName"];
00893 $_SESSION['wsUserName'] = $sName;
00894 } else {
00895 $this->loadDefaults();
00896 return false;
00897 }
00898
00899 $passwordCorrect = FALSE;
00900 $this->mId = $sId;
00901 if ( !$this->loadFromId() ) {
00902 # Not a valid ID, loadFromId has switched the object to anon for us
00903 return false;
00904 }
00905
00906 global $wgBlockDisablesLogin;
00907 if( $wgBlockDisablesLogin && $this->isBlocked() ) {
00908 # User blocked and we've disabled blocked user logins
00909 $this->loadDefaults();
00910 return false;
00911 }
00912
00913 if ( isset( $_SESSION['wsToken'] ) ) {
00914 $passwordCorrect = $_SESSION['wsToken'] == $this->mToken;
00915 $from = 'session';
00916 } else if ( isset( $_COOKIE["{$wgCookiePrefix}Token"] ) ) {
00917 $passwordCorrect = $this->mToken == $_COOKIE["{$wgCookiePrefix}Token"];
00918 $from = 'cookie';
00919 } else {
00920 # No session or persistent login cookie
00921 $this->loadDefaults();
00922 return false;
00923 }
00924
00925 if ( ( $sName == $this->mName ) && $passwordCorrect ) {
00926 $_SESSION['wsToken'] = $this->mToken;
00927 wfDebug( "Logged in from $from\n" );
00928 return true;
00929 } else {
00930 # Invalid credentials
00931 wfDebug( "Can't log in from $from, invalid credentials\n" );
00932 $this->loadDefaults();
00933 return false;
00934 }
00935 }
00936
00944 function loadFromDatabase() {
00945 # Paranoia
00946 $this->mId = intval( $this->mId );
00947
00949 if( !$this->mId ) {
00950 $this->loadDefaults();
00951 return false;
00952 }
00953
00954 $dbr = wfGetDB( DB_MASTER );
00955 $s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
00956
00957 wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
00958
00959 if ( $s !== false ) {
00960 # Initialise user table data
00961 $this->loadFromRow( $s );
00962 $this->mGroups = null;
00963 $this->getEditCount();
00964 return true;
00965 } else {
00966 # Invalid user_id
00967 $this->mId = 0;
00968 $this->loadDefaults();
00969 return false;
00970 }
00971 }
00972
00978 function loadFromRow( $row ) {
00979 $this->mDataLoaded = true;
00980
00981 if ( isset( $row->user_id ) ) {
00982 $this->mId = intval( $row->user_id );
00983 }
00984 $this->mName = $row->user_name;
00985 $this->mRealName = $row->user_real_name;
00986 $this->mPassword = $row->user_password;
00987 $this->mNewpassword = $row->user_newpassword;
00988 $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time );
00989 $this->mEmail = $row->user_email;
00990 $this->decodeOptions( $row->user_options );
00991 $this->mTouched = wfTimestamp(TS_MW,$row->user_touched);
00992 $this->mToken = $row->user_token;
00993 $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
00994 $this->mEmailToken = $row->user_email_token;
00995 $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
00996 $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
00997 $this->mEditCount = $row->user_editcount;
00998 }
00999
01004 function loadGroups() {
01005 if ( is_null( $this->mGroups ) ) {
01006 $dbr = wfGetDB( DB_MASTER );
01007 $res = $dbr->select( 'user_groups',
01008 array( 'ug_group' ),
01009 array( 'ug_user' => $this->mId ),
01010 __METHOD__ );
01011 $this->mGroups = array();
01012 while( $row = $dbr->fetchObject( $res ) ) {
01013 $this->mGroups[] = $row->ug_group;
01014 }
01015 }
01016 }
01017
01024 function clearInstanceCache( $reloadFrom = false ) {
01025 $this->mNewtalk = -1;
01026 $this->mDatePreference = null;
01027 $this->mBlockedby = -1; # Unset
01028 $this->mHash = false;
01029 $this->mSkin = null;
01030 $this->mRights = null;
01031 $this->mEffectiveGroups = null;
01032 $this->mOptions = null;
01033
01034 if ( $reloadFrom ) {
01035 $this->mDataLoaded = false;
01036 $this->mFrom = $reloadFrom;
01037 }
01038 }
01039
01046 static function getDefaultOptions() {
01047 global $wgNamespacesToBeSearchedDefault;
01051 global $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin;
01052 $defOpt = $wgDefaultUserOptions + $wgContLang->getDefaultUserOptionOverrides();
01053
01057 $variant = $wgContLang->getPreferredVariant( false );
01058 $defOpt['variant'] = $variant;
01059 $defOpt['language'] = $variant;
01060 foreach( SearchEngine::searchableNamespaces() as $nsnum => $nsname ) {
01061 $defOpt['searchNs'.$nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
01062 }
01063 $defOpt['skin'] = $wgDefaultSkin;
01064
01065 return $defOpt;
01066 }
01067
01074 public static function getDefaultOption( $opt ) {
01075 $defOpts = self::getDefaultOptions();
01076 if( isset( $defOpts[$opt] ) ) {
01077 return $defOpts[$opt];
01078 } else {
01079 return null;
01080 }
01081 }
01082
01087 static function getToggles() {
01088 global $wgContLang, $wgUseRCPatrol;
01089 $extraToggles = array();
01090 wfRunHooks( 'UserToggles', array( &$extraToggles ) );
01091 if( $wgUseRCPatrol ) {
01092 $extraToggles[] = 'hidepatrolled';
01093 $extraToggles[] = 'newpageshidepatrolled';
01094 $extraToggles[] = 'watchlisthidepatrolled';
01095 }
01096 return array_merge( self::$mToggles, $extraToggles, $wgContLang->getExtraUserToggles() );
01097 }
01098
01099
01108 function getBlockedStatus( $bFromSlave = true ) {
01109 global $wgProxyWhitelist, $wgUser;
01110
01111 if ( -1 != $this->mBlockedby ) {
01112 wfDebug( "User::getBlockedStatus: already loaded.\n" );
01113 return;
01114 }
01115
01116 wfProfileIn( __METHOD__ );
01117 wfDebug( __METHOD__.": checking...\n" );
01118
01119
01120
01121
01122
01123
01124 $this->load();
01125
01126 $this->mBlockedby = 0;
01127 $this->mHideName = 0;
01128 $this->mAllowUsertalk = 0;
01129
01130 # Check if we are looking at an IP or a logged-in user
01131 if ( $this->isIP( $this->getName() ) ) {
01132 $ip = $this->getName();
01133 } else {
01134 # Check if we are looking at the current user
01135 # If we don't, and the user is logged in, we don't know about
01136 # his IP / autoblock status, so ignore autoblock of current user's IP
01137 if ( $this->getID() != $wgUser->getID() ) {
01138 $ip = '';
01139 } else {
01140 # Get IP of current user
01141 $ip = wfGetIP();
01142 }
01143 }
01144
01145 if ( $this->isAllowed( 'ipblock-exempt' ) ) {
01146 # Exempt from all types of IP-block
01147 $ip = '';
01148 }
01149
01150 # User/IP blocking
01151 $this->mBlock = new Block();
01152 $this->mBlock->fromMaster( !$bFromSlave );
01153 if ( $this->mBlock->load( $ip , $this->mId ) ) {
01154 wfDebug( __METHOD__ . ": Found block.\n" );
01155 $this->mBlockedby = $this->mBlock->mBy;
01156 if( $this->mBlockedby == "0" )
01157 $this->mBlockedby = $this->mBlock->mByName;
01158 $this->mBlockreason = $this->mBlock->mReason;
01159 $this->mHideName = $this->mBlock->mHideName;
01160 $this->mAllowUsertalk = $this->mBlock->mAllowUsertalk;
01161 if ( $this->isLoggedIn() && $wgUser->getID() == $this->getID() ) {
01162 $this->spreadBlock();
01163 }
01164 } else {
01165
01166
01167
01168 }
01169
01170 # Proxy blocking
01171 if ( !$this->isAllowed( 'proxyunbannable' ) && !in_array( $ip, $wgProxyWhitelist ) ) {
01172 # Local list
01173 if ( wfIsLocallyBlockedProxy( $ip ) ) {
01174 $this->mBlockedby = wfMsg( 'proxyblocker' );
01175 $this->mBlockreason = wfMsg( 'proxyblockreason' );
01176 }
01177
01178 # DNSBL
01179 if ( !$this->mBlockedby && !$this->getID() ) {
01180 if ( $this->isDnsBlacklisted( $ip ) ) {
01181 $this->mBlockedby = wfMsg( 'sorbs' );
01182 $this->mBlockreason = wfMsg( 'sorbsreason' );
01183 }
01184 }
01185 }
01186
01187 # Extensions
01188 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
01189
01190 wfProfileOut( __METHOD__ );
01191 }
01192
01200 function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
01201 global $wgEnableSorbs, $wgEnableDnsBlacklist,
01202 $wgSorbsUrl, $wgDnsBlacklistUrls, $wgProxyWhitelist;
01203
01204 if ( !$wgEnableDnsBlacklist && !$wgEnableSorbs )
01205 return false;
01206
01207 if ( $checkWhitelist && in_array( $ip, $wgProxyWhitelist ) )
01208 return false;
01209
01210 $urls = array_merge( $wgDnsBlacklistUrls, (array)$wgSorbsUrl );
01211 return $this->inDnsBlacklist( $ip, $urls );
01212 }
01213
01221 function inDnsBlacklist( $ip, $bases ) {
01222 wfProfileIn( __METHOD__ );
01223
01224 $found = false;
01225 $host = '';
01226
01227 if( IP::isIPv4( $ip ) ) {
01228 # Reverse IP, bug 21255
01229 $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
01230
01231 foreach( (array)$bases as $base ) {
01232 # Make hostname
01233 $host = "$ipReversed.$base";
01234
01235 # Send query
01236 $ipList = gethostbynamel( $host );
01237
01238 if( $ipList ) {
01239 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
01240 $found = true;
01241 break;
01242 } else {
01243 wfDebug( "Requested $host, not found in $base.\n" );
01244 }
01245 }
01246 }
01247
01248 wfProfileOut( __METHOD__ );
01249 return $found;
01250 }
01251
01257 public function isPingLimitable() {
01258 global $wgRateLimitsExcludedGroups;
01259 global $wgRateLimitsExcludedIPs;
01260 if( array_intersect( $this->getEffectiveGroups(), $wgRateLimitsExcludedGroups ) ) {
01261
01262 return false;
01263 }
01264 if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) {
01265
01266
01267
01268 return false;
01269 }
01270 return !$this->isAllowed('noratelimit');
01271 }
01272
01283 function pingLimiter( $action = 'edit' ) {
01284 # Call the 'PingLimiter' hook
01285 $result = false;
01286 if( !wfRunHooks( 'PingLimiter', array( &$this, $action, $result ) ) ) {
01287 return $result;
01288 }
01289
01290 global $wgRateLimits;
01291 if( !isset( $wgRateLimits[$action] ) ) {
01292 return false;
01293 }
01294
01295 # Some groups shouldn't trigger the ping limiter, ever
01296 if( !$this->isPingLimitable() )
01297 return false;
01298
01299 global $wgMemc, $wgRateLimitLog;
01300 wfProfileIn( __METHOD__ );
01301
01302 $limits = $wgRateLimits[$action];
01303 $keys = array();
01304 $id = $this->getId();
01305 $ip = wfGetIP();
01306 $userLimit = false;
01307
01308 if( isset( $limits['anon'] ) && $id == 0 ) {
01309 $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
01310 }
01311
01312 if( isset( $limits['user'] ) && $id != 0 ) {
01313 $userLimit = $limits['user'];
01314 }
01315 if( $this->isNewbie() ) {
01316 if( isset( $limits['newbie'] ) && $id != 0 ) {
01317 $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
01318 }
01319 if( isset( $limits['ip'] ) ) {
01320 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
01321 }
01322 $matches = array();
01323 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
01324 $subnet = $matches[1];
01325 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
01326 }
01327 }
01328
01329
01330 foreach ( $this->getGroups() as $group ) {
01331 if ( isset( $limits[$group] ) ) {
01332 if ( $userLimit === false || $limits[$group] > $userLimit ) {
01333 $userLimit = $limits[$group];
01334 }
01335 }
01336 }
01337
01338 if ( $userLimit !== false ) {
01339 wfDebug( __METHOD__ . ": effective user limit: $userLimit\n" );
01340 $keys[ wfMemcKey( 'limiter', $action, 'user', $id ) ] = $userLimit;
01341 }
01342
01343 $triggered = false;
01344 foreach( $keys as $key => $limit ) {
01345 list( $max, $period ) = $limit;
01346 $summary = "(limit $max in {$period}s)";
01347 $count = $wgMemc->get( $key );
01348
01349 if( $count ) {
01350 if( $count > $max ) {
01351 wfDebug( __METHOD__ . ": tripped! $key at $count $summary\n" );
01352 if( $wgRateLimitLog ) {
01353 @error_log( wfTimestamp( TS_MW ) . ' ' . wfWikiID() . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
01354 }
01355 $triggered = true;
01356 } else {
01357 wfDebug( __METHOD__ . ": ok. $key at $count $summary\n" );
01358 }
01359 } else {
01360 wfDebug( __METHOD__ . ": adding record for $key $summary\n" );
01361 $wgMemc->add( $key, 0, intval( $period ) );
01362 }
01363 $wgMemc->incr( $key );
01364 }
01365
01366 wfProfileOut( __METHOD__ );
01367 return $triggered;
01368 }
01369
01376 function isBlocked( $bFromSlave = true ) {
01377 wfDebug( "User::isBlocked: enter\n" );
01378 $this->getBlockedStatus( $bFromSlave );
01379 return $this->mBlockedby !== 0;
01380 }
01381
01389 function isBlockedFrom( $title, $bFromSlave = false ) {
01390 global $wgBlockAllowsUTEdit;
01391 wfProfileIn( __METHOD__ );
01392 wfDebug( __METHOD__ . ": enter\n" );
01393
01394 wfDebug( __METHOD__ . ": asking isBlocked()\n" );
01395 $blocked = $this->isBlocked( $bFromSlave );
01396 $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
01397 # If a user's name is suppressed, they cannot make edits anywhere
01398 if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName() &&
01399 $title->getNamespace() == NS_USER_TALK ) {
01400 $blocked = false;
01401 wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
01402 }
01403
01404 wfRunHooks( 'UserIsBlockedFrom', array( $this, $title, &$blocked, &$allowUsertalk ) );
01405
01406 wfProfileOut( __METHOD__ );
01407 return $blocked;
01408 }
01409
01414 function blockedBy() {
01415 $this->getBlockedStatus();
01416 return $this->mBlockedby;
01417 }
01418
01423 function blockedFor() {
01424 $this->getBlockedStatus();
01425 return $this->mBlockreason;
01426 }
01427
01432 function getBlockId() {
01433 $this->getBlockedStatus();
01434 return ( $this->mBlock ? $this->mBlock->mId : false );
01435 }
01436
01445 function isBlockedGlobally( $ip = '' ) {
01446 if( $this->mBlockedGlobally !== null ) {
01447 return $this->mBlockedGlobally;
01448 }
01449
01450 if( IP::isIPAddress( $this->getName() ) ) {
01451 $ip = $this->getName();
01452 } else if( !$ip ) {
01453 $ip = wfGetIP();
01454 }
01455 $blocked = false;
01456 wfRunHooks( 'UserIsBlockedGlobally', array( &$this, $ip, &$blocked ) );
01457 $this->mBlockedGlobally = (bool)$blocked;
01458 return $this->mBlockedGlobally;
01459 }
01460
01466 function isLocked() {
01467 if( $this->mLocked !== null ) {
01468 return $this->mLocked;
01469 }
01470 global $wgAuth;
01471 $authUser = $wgAuth->getUserInstance( $this );
01472 $this->mLocked = (bool)$authUser->isLocked();
01473 return $this->mLocked;
01474 }
01475
01481 function isHidden() {
01482 if( $this->mHideName !== null ) {
01483 return $this->mHideName;
01484 }
01485 $this->getBlockedStatus();
01486 if( !$this->mHideName ) {
01487 global $wgAuth;
01488 $authUser = $wgAuth->getUserInstance( $this );
01489 $this->mHideName = (bool)$authUser->isHidden();
01490 }
01491 return $this->mHideName;
01492 }
01493
01498 function getId() {
01499 if( $this->mId === null and $this->mName !== null
01500 and User::isIP( $this->mName ) ) {
01501
01502 return 0;
01503 } elseif( $this->mId === null ) {
01504
01505 $this->load();
01506 }
01507 return $this->mId;
01508 }
01509
01514 function setId( $v ) {
01515 $this->mId = $v;
01516 $this->clearInstanceCache( 'id' );
01517 }
01518
01523 function getName() {
01524 if ( !$this->mDataLoaded && $this->mFrom == 'name' ) {
01525 # Special case optimisation
01526 return $this->mName;
01527 } else {
01528 $this->load();
01529 if ( $this->mName === false ) {
01530 # Clean up IPs
01531 $this->mName = IP::sanitizeIP( wfGetIP() );
01532 }
01533 return $this->mName;
01534 }
01535 }
01536
01550 function setName( $str ) {
01551 $this->load();
01552 $this->mName = $str;
01553 }
01554
01559 function getTitleKey() {
01560 return str_replace( ' ', '_', $this->getName() );
01561 }
01562
01567 function getNewtalk() {
01568 $this->load();
01569
01570 # Load the newtalk status if it is unloaded (mNewtalk=-1)
01571 if( $this->mNewtalk === -1 ) {
01572 $this->mNewtalk = false; # reset talk page status
01573
01574 # Check memcached separately for anons, who have no
01575 # entire User object stored in there.
01576 if( !$this->mId ) {
01577 global $wgMemc;
01578 $key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
01579 $newtalk = $wgMemc->get( $key );
01580 if( strval( $newtalk ) !== '' ) {
01581 $this->mNewtalk = (bool)$newtalk;
01582 } else {
01583
01584
01585 $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName(), true );
01586 $wgMemc->set( $key, (int)$this->mNewtalk, 1800 );
01587 }
01588 } else {
01589 $this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
01590 }
01591 }
01592
01593 return (bool)$this->mNewtalk;
01594 }
01595
01600 function getNewMessageLinks() {
01601 $talks = array();
01602 if( !wfRunHooks( 'UserRetrieveNewTalks', array( &$this, &$talks ) ) )
01603 return $talks;
01604
01605 if( !$this->getNewtalk() )
01606 return array();
01607 $up = $this->getUserPage();
01608 $utp = $up->getTalkPage();
01609 return array( array( 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL() ) );
01610 }
01611
01622 function checkNewtalk( $field, $id, $fromMaster = false ) {
01623 if ( $fromMaster ) {
01624 $db = wfGetDB( DB_MASTER );
01625 } else {
01626 $db = wfGetDB( DB_SLAVE );
01627 }
01628 $ok = $db->selectField( 'user_newtalk', $field,
01629 array( $field => $id ), __METHOD__ );
01630 return $ok !== false;
01631 }
01632
01640 function updateNewtalk( $field, $id ) {
01641 $dbw = wfGetDB( DB_MASTER );
01642 $dbw->insert( 'user_newtalk',
01643 array( $field => $id ),
01644 __METHOD__,
01645 'IGNORE' );
01646 if ( $dbw->affectedRows() ) {
01647 wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
01648 return true;
01649 } else {
01650 wfDebug( __METHOD__ . " already set ($field, $id)\n" );
01651 return false;
01652 }
01653 }
01654
01662 function deleteNewtalk( $field, $id ) {
01663 $dbw = wfGetDB( DB_MASTER );
01664 $dbw->delete( 'user_newtalk',
01665 array( $field => $id ),
01666 __METHOD__ );
01667 if ( $dbw->affectedRows() ) {
01668 wfDebug( __METHOD__ . ": killed on ($field, $id)\n" );
01669 return true;
01670 } else {
01671 wfDebug( __METHOD__ . ": already gone ($field, $id)\n" );
01672 return false;
01673 }
01674 }
01675
01680 function setNewtalk( $val ) {
01681 if( wfReadOnly() ) {
01682 return;
01683 }
01684
01685 $this->load();
01686 $this->mNewtalk = $val;
01687
01688 if( $this->isAnon() ) {
01689 $field = 'user_ip';
01690 $id = $this->getName();
01691 } else {
01692 $field = 'user_id';
01693 $id = $this->getId();
01694 }
01695 global $wgMemc;
01696
01697 if( $val ) {
01698 $changed = $this->updateNewtalk( $field, $id );
01699 } else {
01700 $changed = $this->deleteNewtalk( $field, $id );
01701 }
01702
01703 if( $this->isAnon() ) {
01704
01705
01706 $key = wfMemcKey( 'newtalk', 'ip', $id );
01707 $wgMemc->set( $key, $val ? 1 : 0, 1800 );
01708 }
01709 if ( $changed ) {
01710 $this->invalidateCache();
01711 }
01712 }
01713
01719 private static function newTouchedTimestamp() {
01720 global $wgClockSkewFudge;
01721 return wfTimestamp( TS_MW, time() + $wgClockSkewFudge );
01722 }
01723
01731 private function clearSharedCache() {
01732 $this->load();
01733 if( $this->mId ) {
01734 global $wgMemc;
01735 $wgMemc->delete( wfMemcKey( 'user', 'id', $this->mId ) );
01736 }
01737 }
01738
01744 function invalidateCache() {
01745 if( wfReadOnly() ) {
01746 return;
01747 }
01748 $this->load();
01749 if( $this->mId ) {
01750 $this->mTouched = self::newTouchedTimestamp();
01751
01752 $dbw = wfGetDB( DB_MASTER );
01753 $dbw->update( 'user',
01754 array( 'user_touched' => $dbw->timestamp( $this->mTouched ) ),
01755 array( 'user_id' => $this->mId ),
01756 __METHOD__ );
01757
01758 $this->clearSharedCache();
01759 }
01760 }
01761
01766 function validateCache( $timestamp ) {
01767 $this->load();
01768 return ( $timestamp >= $this->mTouched );
01769 }
01770
01774 function getTouched() {
01775 $this->load();
01776 return $this->mTouched;
01777 }
01778
01793 function setPassword( $str ) {
01794 global $wgAuth;
01795
01796 if( $str !== null ) {
01797 if( !$wgAuth->allowPasswordChange() ) {
01798 throw new PasswordError( wfMsg( 'password-change-forbidden' ) );
01799 }
01800
01801 if( !$this->isValidPassword( $str ) ) {
01802 global $wgMinimalPasswordLength;
01803 $valid = $this->getPasswordValidity( $str );
01804 throw new PasswordError( wfMsgExt( $valid, array( 'parsemag' ),
01805 $wgMinimalPasswordLength ) );
01806 }
01807 }
01808
01809 if( !$wgAuth->setPassword( $this, $str ) ) {
01810 throw new PasswordError( wfMsg( 'externaldberror' ) );
01811 }
01812
01813 $this->setInternalPassword( $str );
01814
01815 return true;
01816 }
01817
01823 function setInternalPassword( $str ) {
01824 $this->load();
01825 $this->setToken();
01826
01827 if( $str === null ) {
01828
01829 $this->mPassword = '';
01830 } else {
01831 $this->mPassword = self::crypt( $str );
01832 }
01833 $this->mNewpassword = '';
01834 $this->mNewpassTime = null;
01835 }
01836
01841 function getToken() {
01842 $this->load();
01843 return $this->mToken;
01844 }
01845
01853 function setToken( $token = false ) {
01854 global $wgSecretKey, $wgProxyKey;
01855 $this->load();
01856 if ( !$token ) {
01857 if ( $wgSecretKey ) {
01858 $key = $wgSecretKey;
01859 } elseif ( $wgProxyKey ) {
01860 $key = $wgProxyKey;
01861 } else {
01862 $key = microtime();
01863 }
01864 $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId );
01865 } else {
01866 $this->mToken = $token;
01867 }
01868 }
01869
01876 function setCookiePassword( $str ) {
01877 $this->load();
01878 $this->mCookiePassword = md5( $str );
01879 }
01880
01887 function setNewpassword( $str, $throttle = true ) {
01888 $this->load();
01889 $this->mNewpassword = self::crypt( $str );
01890 if ( $throttle ) {
01891 $this->mNewpassTime = wfTimestampNow();
01892 }
01893 }
01894
01900 function isPasswordReminderThrottled() {
01901 global $wgPasswordReminderResendTime;
01902 $this->load();
01903 if ( !$this->mNewpassTime || !$wgPasswordReminderResendTime ) {
01904 return false;
01905 }
01906 $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgPasswordReminderResendTime * 3600;
01907 return time() < $expiry;
01908 }
01909
01914 function getEmail() {
01915 $this->load();
01916 wfRunHooks( 'UserGetEmail', array( $this, &$this->mEmail ) );
01917 return $this->mEmail;
01918 }
01919
01924 function getEmailAuthenticationTimestamp() {
01925 $this->load();
01926 wfRunHooks( 'UserGetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
01927 return $this->mEmailAuthenticated;
01928 }
01929
01934 function setEmail( $str ) {
01935 $this->load();
01936 $this->mEmail = $str;
01937 wfRunHooks( 'UserSetEmail', array( $this, &$this->mEmail ) );
01938 }
01939
01944 function getRealName() {
01945 $this->load();
01946 return $this->mRealName;
01947 }
01948
01953 function setRealName( $str ) {
01954 $this->load();
01955 $this->mRealName = $str;
01956 }
01957
01967 function getOption( $oname, $defaultOverride = null ) {
01968 $this->loadOptions();
01969
01970 if ( is_null( $this->mOptions ) ) {
01971 if($defaultOverride != '') {
01972 return $defaultOverride;
01973 }
01974 $this->mOptions = User::getDefaultOptions();
01975 }
01976
01977 if ( array_key_exists( $oname, $this->mOptions ) ) {
01978 return $this->mOptions[$oname];
01979 } else {
01980 return $defaultOverride;
01981 }
01982 }
01983
01989 public function getOptions() {
01990 $this->loadOptions();
01991 return $this->mOptions;
01992 }
01993
02001 function getBoolOption( $oname ) {
02002 return (bool)$this->getOption( $oname );
02003 }
02004
02005
02014 function getIntOption( $oname, $defaultOverride=0 ) {
02015 $val = $this->getOption( $oname );
02016 if( $val == '' ) {
02017 $val = $defaultOverride;
02018 }
02019 return intval( $val );
02020 }
02021
02028 function setOption( $oname, $val ) {
02029 $this->load();
02030 $this->loadOptions();
02031
02032 if ( $oname == 'skin' ) {
02033 # Clear cached skin, so the new one displays immediately in Special:Preferences
02034 unset( $this->mSkin );
02035 }
02036
02037
02038 global $wgDefaultUserOptions;
02039 if( is_null( $val ) && isset( $wgDefaultUserOptions[$oname] ) ) {
02040 $val = $wgDefaultUserOptions[$oname];
02041 }
02042
02043 $this->mOptions[$oname] = $val;
02044 }
02045
02049 function resetOptions() {
02050 $this->mOptions = User::getDefaultOptions();
02051 }
02052
02057 function getDatePreference() {
02058
02059 if ( is_null( $this->mDatePreference ) ) {
02060 global $wgLang;
02061 $value = $this->getOption( 'date' );
02062 $map = $wgLang->getDatePreferenceMigrationMap();
02063 if ( isset( $map[$value] ) ) {
02064 $value = $map[$value];
02065 }
02066 $this->mDatePreference = $value;
02067 }
02068 return $this->mDatePreference;
02069 }
02070
02075 function getRights() {
02076 if ( is_null( $this->mRights ) ) {
02077 $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
02078 wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
02079
02080 $this->mRights = array_values( $this->mRights );
02081 }
02082 return $this->mRights;
02083 }
02084
02090 function getGroups() {
02091 $this->load();
02092 return $this->mGroups;
02093 }
02094
02102 function getEffectiveGroups( $recache = false ) {
02103 if ( $recache || is_null( $this->mEffectiveGroups ) ) {
02104 wfProfileIn( __METHOD__ );
02105 $this->mEffectiveGroups = $this->getGroups();
02106 $this->mEffectiveGroups[] = '*';
02107 if( $this->getId() ) {
02108 $this->mEffectiveGroups[] = 'user';
02109
02110 $this->mEffectiveGroups = array_unique( array_merge(
02111 $this->mEffectiveGroups,
02112 Autopromote::getAutopromoteGroups( $this )
02113 ) );
02114
02115 # Hook for additional groups
02116 wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
02117 }
02118 wfProfileOut( __METHOD__ );
02119 }
02120 return $this->mEffectiveGroups;
02121 }
02122
02127 function getEditCount() {
02128 if( $this->getId() ) {
02129 if ( !isset( $this->mEditCount ) ) {
02130
02131 $this->mEditCount = User::edits( $this->mId );
02132 }
02133 return $this->mEditCount;
02134 } else {
02135
02136 return null;
02137 }
02138 }
02139
02145 function addGroup( $group ) {
02146 $dbw = wfGetDB( DB_MASTER );
02147 if( $this->getId() ) {
02148 $dbw->insert( 'user_groups',
02149 array(
02150 'ug_user' => $this->getID(),
02151 'ug_group' => $group,
02152 ),
02153 'User::addGroup',
02154 array( 'IGNORE' ) );
02155 }
02156
02157 $this->loadGroups();
02158 $this->mGroups[] = $group;
02159 $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
02160
02161 $this->invalidateCache();
02162 }
02163
02169 function removeGroup( $group ) {
02170 $this->load();
02171 $dbw = wfGetDB( DB_MASTER );
02172 $dbw->delete( 'user_groups',
02173 array(
02174 'ug_user' => $this->getID(),
02175 'ug_group' => $group,
02176 ),
02177 'User::removeGroup' );
02178
02179 $this->loadGroups();
02180 $this->mGroups = array_diff( $this->mGroups, array( $group ) );
02181 $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
02182
02183 $this->invalidateCache();
02184 }
02185
02190 function isLoggedIn() {
02191 return $this->getID() != 0;
02192 }
02193
02198 function isAnon() {
02199 return !$this->isLoggedIn();
02200 }
02201
02207 function isBot() {
02208 wfDeprecated( __METHOD__ );
02209 return $this->isAllowed( 'bot' );
02210 }
02211
02217 function isAllowed( $action = '' ) {
02218 if ( $action === '' )
02219 return true;
02220 # Patrolling may not be enabled
02221 if( $action === 'patrol' || $action === 'autopatrol' ) {
02222 global $wgUseRCPatrol, $wgUseNPPatrol;
02223 if( !$wgUseRCPatrol && !$wgUseNPPatrol )
02224 return false;
02225 }
02226 # Use strict parameter to avoid matching numeric 0 accidentally inserted
02227 # by misconfiguration: 0 == 'foo'
02228 return in_array( $action, $this->getRights(), true );
02229 }
02230
02235 public function useRCPatrol() {
02236 global $wgUseRCPatrol;
02237 return( $wgUseRCPatrol && ( $this->isAllowed( 'patrol' ) || $this->isAllowed( 'patrolmarks' ) ) );
02238 }
02239
02244 public function useNPPatrol() {
02245 global $wgUseRCPatrol, $wgUseNPPatrol;
02246 return( ( $wgUseRCPatrol || $wgUseNPPatrol ) && ( $this->isAllowed( 'patrol' ) || $this->isAllowed( 'patrolmarks' ) ) );
02247 }
02248
02255 function &getSkin( $t = null ) {
02256 if ( !isset( $this->mSkin ) ) {
02257 wfProfileIn( __METHOD__ );
02258
02259 global $wgHiddenPrefs;
02260 if( !in_array( 'skin', $wgHiddenPrefs ) ) {
02261 # get the user skin
02262 global $wgRequest;
02263 $userSkin = $this->getOption( 'skin' );
02264 $userSkin = $wgRequest->getVal( 'useskin', $userSkin );
02265 } else {
02266 # if we're not allowing users to override, then use the default
02267 global $wgDefaultSkin;
02268 $userSkin = $wgDefaultSkin;
02269 }
02270
02271 $this->mSkin =& Skin::newFromKey( $userSkin );
02272 wfProfileOut( __METHOD__ );
02273 }
02274 if( $t || !$this->mSkin->getTitle() ) {
02275 if ( !$t ) {
02276 global $wgOut;
02277 $t = $wgOut->getTitle();
02278 }
02279 $this->mSkin->setTitle( $t );
02280 }
02281 return $this->mSkin;
02282 }
02283
02289 function isWatched( $title ) {
02290 $wl = WatchedItem::fromUserTitle( $this, $title );
02291 return $wl->isWatched();
02292 }
02293
02298 function addWatch( $title ) {
02299 $wl = WatchedItem::fromUserTitle( $this, $title );
02300 $wl->addWatch();
02301 $this->invalidateCache();
02302 }
02303
02308 function removeWatch( $title ) {
02309 $wl = WatchedItem::fromUserTitle( $this, $title );
02310 $wl->removeWatch();
02311 $this->invalidateCache();
02312 }
02313
02320 function clearNotification( &$title ) {
02321 global $wgUser, $wgUseEnotif, $wgShowUpdatedMarker;
02322
02323 # Do nothing if the database is locked to writes
02324 if( wfReadOnly() ) {
02325 return;
02326 }
02327
02328 if( $title->getNamespace() == NS_USER_TALK &&
02329 $title->getText() == $this->getName() ) {
02330 if( !wfRunHooks( 'UserClearNewTalkNotification', array( &$this ) ) )
02331 return;
02332 $this->setNewtalk( false );
02333 }
02334
02335 if( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
02336 return;
02337 }
02338
02339 if( $this->isAnon() ) {
02340
02341 return;
02342 }
02343
02344
02345
02346
02347
02348 if( $title->getNamespace() == NS_USER_TALK &&
02349 $title->getText() == $wgUser->getName() )
02350 {
02351 $watched = true;
02352 } elseif ( $this->getId() == $wgUser->getId() ) {
02353 $watched = $title->userIsWatching();
02354 } else {
02355 $watched = true;
02356 }
02357
02358
02359
02360 if ( $watched ) {
02361 $dbw = wfGetDB( DB_MASTER );
02362 $dbw->update( 'watchlist',
02363 array(
02364 'wl_notificationtimestamp' => null
02365 ), array(
02366 'wl_title' => $title->getDBkey(),
02367 'wl_namespace' => $title->getNamespace(),
02368 'wl_user' => $this->getID()
02369 ), __METHOD__
02370 );
02371 }
02372 }
02373
02381 function clearAllNotifications( $currentUser ) {
02382 global $wgUseEnotif, $wgShowUpdatedMarker;
02383 if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
02384 $this->setNewtalk( false );
02385 return;
02386 }
02387 if( $currentUser != 0 ) {
02388 $dbw = wfGetDB( DB_MASTER );
02389 $dbw->update( 'watchlist',
02390 array(
02391 'wl_notificationtimestamp' => null
02392 ), array(
02393 'wl_user' => $currentUser
02394 ), __METHOD__
02395 );
02396 # We also need to clear here the "you have new message" notification for the own user_talk page
02397 # This is cleared one page view later in Article::viewUpdates();
02398 }
02399 }
02400
02406 function decodeOptions( $str ) {
02407 if( !$str )
02408 return;
02409
02410 $this->mOptionsLoaded = true;
02411 $this->mOptionOverrides = array();
02412
02413 $this->mOptions = array();
02414 $a = explode( "\n", $str );
02415 foreach ( $a as $s ) {
02416 $m = array();
02417 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
02418 $this->mOptions[$m[1]] = $m[2];
02419 $this->mOptionOverrides[$m[1]] = $m[2];
02420 }
02421 }
02422 }
02423
02432 protected function setCookie( $name, $value, $exp = 0 ) {
02433 global $wgRequest;
02434 $wgRequest->response()->setcookie( $name, $value, $exp );
02435 }
02436
02441 protected function clearCookie( $name ) {
02442 $this->setCookie( $name, '', time() - 86400 );
02443 }
02444
02448 function setCookies() {
02449 $this->load();
02450 if ( 0 == $this->mId ) return;
02451 $session = array(
02452 'wsUserID' => $this->mId,
02453 'wsToken' => $this->mToken,
02454 'wsUserName' => $this->getName()
02455 );
02456 $cookies = array(
02457 'UserID' => $this->mId,
02458 'UserName' => $this->getName(),
02459 );
02460 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
02461 $cookies['Token'] = $this->mToken;
02462 } else {
02463 $cookies['Token'] = false;
02464 }
02465
02466 wfRunHooks( 'UserSetCookies', array( $this, &$session, &$cookies ) );
02467 #check for null, since the hook could cause a null value
02468 if ( !is_null( $session ) && isset( $_SESSION ) ){
02469 $_SESSION = $session + $_SESSION;
02470 }
02471 foreach ( $cookies as $name => $value ) {
02472 if ( $value === false ) {
02473 $this->clearCookie( $name );
02474 } else {
02475 $this->setCookie( $name, $value );
02476 }
02477 }
02478 }
02479
02483 function logout() {
02484 if( wfRunHooks( 'UserLogout', array( &$this ) ) ) {
02485 $this->doLogout();
02486 }
02487 }
02488
02494 function doLogout() {
02495 $this->clearInstanceCache( 'defaults' );
02496
02497 $_SESSION['wsUserID'] = 0;
02498
02499 $this->clearCookie( 'UserID' );
02500 $this->clearCookie( 'Token' );
02501
02502 # Remember when user logged out, to prevent seeing cached pages
02503 $this->setCookie( 'LoggedOut', wfTimestampNow(), time() + 86400 );
02504 }
02505
02510 function saveSettings() {
02511 $this->load();
02512 if ( wfReadOnly() ) { return; }
02513 if ( 0 == $this->mId ) { return; }
02514
02515 $this->mTouched = self::newTouchedTimestamp();
02516
02517 $dbw = wfGetDB( DB_MASTER );
02518 $dbw->update( 'user',
02519 array(
02520 'user_name' => $this->mName,
02521 'user_password' => $this->mPassword,
02522 'user_newpassword' => $this->mNewpassword,
02523 'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
02524 'user_real_name' => $this->mRealName,
02525 'user_email' => $this->mEmail,
02526 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
02527 'user_options' => '',
02528 'user_touched' => $dbw->timestamp( $this->mTouched ),
02529 'user_token' => $this->mToken,
02530 'user_email_token' => $this->mEmailToken,
02531 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
02532 ), array(
02533 'user_id' => $this->mId
02534 ), __METHOD__
02535 );
02536
02537 $this->saveOptions();
02538
02539 wfRunHooks( 'UserSaveSettings', array( $this ) );
02540 $this->clearSharedCache();
02541 $this->getUserPage()->invalidateCache();
02542 }
02543
02547 function idForName() {
02548 $s = trim( $this->getName() );
02549 if ( $s === '' ) return 0;
02550
02551 $dbr = wfGetDB( DB_SLAVE );
02552 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
02553 if ( $id === false ) {
02554 $id = 0;
02555 }
02556 return $id;
02557 }
02558
02575 static function createNew( $name, $params = array() ) {
02576 $user = new User;
02577 $user->load();
02578 if ( isset( $params['options'] ) ) {
02579 $user->mOptions = $params['options'] + (array)$user->mOptions;
02580 unset( $params['options'] );
02581 }
02582 $dbw = wfGetDB( DB_MASTER );
02583 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
02584 $fields = array(
02585 'user_id' => $seqVal,
02586 'user_name' => $name,
02587 'user_password' => $user->mPassword,
02588 'user_newpassword' => $user->mNewpassword,
02589 'user_newpass_time' => $dbw->timestamp( $user->mNewpassTime ),
02590 'user_email' => $user->mEmail,
02591 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
02592 'user_real_name' => $user->mRealName,
02593 'user_options' => '',
02594 'user_token' => $user->mToken,
02595 'user_registration' => $dbw->timestamp( $user->mRegistration ),
02596 'user_editcount' => 0,
02597 );
02598 foreach ( $params as $name => $value ) {
02599 $fields["user_$name"] = $value;
02600 }
02601 $dbw->insert( 'user', $fields, __METHOD__, array( 'IGNORE' ) );
02602 if ( $dbw->affectedRows() ) {
02603 $newUser = User::newFromId( $dbw->insertId() );
02604 } else {
02605 $newUser = null;
02606 }
02607 return $newUser;
02608 }
02609
02613 function addToDatabase() {
02614 $this->load();
02615 $dbw = wfGetDB( DB_MASTER );
02616 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
02617 $dbw->insert( 'user',
02618 array(
02619 'user_id' => $seqVal,
02620 'user_name' => $this->mName,
02621 'user_password' => $this->mPassword,
02622 'user_newpassword' => $this->mNewpassword,
02623 'user_newpass_time' => $dbw->timestamp( $this->mNewpassTime ),
02624 'user_email' => $this->mEmail,
02625 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
02626 'user_real_name' => $this->mRealName,
02627 'user_options' => '',
02628 'user_token' => $this->mToken,
02629 'user_registration' => $dbw->timestamp( $this->mRegistration ),
02630 'user_editcount' => 0,
02631 ), __METHOD__
02632 );
02633 $this->mId = $dbw->insertId();
02634
02635
02636 $this->clearInstanceCache();
02637
02638 $this->saveOptions();
02639 }
02640
02645 function spreadBlock() {
02646 wfDebug( __METHOD__ . "()\n" );
02647 $this->load();
02648 if ( $this->mId == 0 ) {
02649 return;
02650 }
02651
02652 $userblock = Block::newFromDB( '', $this->mId );
02653 if ( !$userblock ) {
02654 return;
02655 }
02656
02657 $userblock->doAutoblock( wfGetIP() );
02658 }
02659
02673 function getPageRenderingHash() {
02674 global $wgUseDynamicDates, $wgRenderHashAppend, $wgLang, $wgContLang;
02675 if( $this->mHash ){
02676 return $this->mHash;
02677 }
02678
02679
02680
02681
02682 $confstr = $this->getOption( 'math' );
02683 $confstr .= '!' . $this->getOption( 'stubthreshold' );
02684 if ( $wgUseDynamicDates ) {
02685 $confstr .= '!' . $this->getDatePreference();
02686 }
02687 $confstr .= '!' . ( $this->getOption( 'numberheadings' ) ? '1' : '' );
02688 $confstr .= '!' . $wgLang->getCode();
02689 $confstr .= '!' . $this->getOption( 'thumbsize' );
02690
02691 $extra = $wgContLang->getExtraHashOptions();
02692 $confstr .= $extra;
02693
02694 $confstr .= $wgRenderHashAppend;
02695
02696
02697
02698 wfRunHooks( 'PageRenderingHash', array( &$confstr ) );
02699
02700
02701 $confstr = str_replace( ' ', '_', $confstr );
02702 $this->mHash = $confstr;
02703 return $confstr;
02704 }
02705
02710 function isBlockedFromCreateAccount() {
02711 $this->getBlockedStatus();
02712 return $this->mBlock && $this->mBlock->mCreateAccount;
02713 }
02714
02719 function isBlockedFromEmailuser() {
02720 $this->getBlockedStatus();
02721 return $this->mBlock && $this->mBlock->mBlockEmail;
02722 }
02723
02728 function isAllowedToCreateAccount() {
02729 return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
02730 }
02731
02735 function setLoaded( $loaded ) {
02736 wfDeprecated( __METHOD__ );
02737 }
02738
02744 function getUserPage() {
02745 return Title::makeTitle( NS_USER, $this->getName() );
02746 }
02747
02753 function getTalkPage() {
02754 $title = $this->getUserPage();
02755 return $title->getTalkPage();
02756 }
02757
02763 function getMaxID() {
02764 static $res;
02765
02766 if ( isset( $res ) )
02767 return $res;
02768 else {
02769 $dbr = wfGetDB( DB_SLAVE );
02770 return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
02771 }
02772 }
02773
02779 function isNewbie() {
02780 return !$this->isAllowed( 'autoconfirmed' );
02781 }
02782
02788 function checkPassword( $password ) {
02789 global $wgAuth;
02790 $this->load();
02791
02792
02793
02794
02795
02796
02797 if( !$this->isValidPassword( $password ) ) {
02798 return false;
02799 }
02800
02801 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
02802 return true;
02803 } elseif( $wgAuth->strict() ) {
02804
02805 return false;
02806 } elseif( $wgAuth->strictUserAuth( $this->getName() ) ) {
02807
02808 return false;
02809 }
02810 if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
02811 return true;
02812 } elseif ( function_exists( 'iconv' ) ) {
02813 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
02814 # Check for this with iconv
02815 $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
02816 if ( self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) ) {
02817 return true;
02818 }
02819 }
02820 return false;
02821 }
02822
02828 function checkTemporaryPassword( $plaintext ) {
02829 global $wgNewPasswordExpiry;
02830 if( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) {
02831 $this->load();
02832 $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgNewPasswordExpiry;
02833 return ( time() < $expiry );
02834 } else {
02835 return false;
02836 }
02837 }
02838
02848 function editToken( $salt = '' ) {
02849 if ( $this->isAnon() ) {
02850 return EDIT_TOKEN_SUFFIX;
02851 } else {
02852 if( !isset( $_SESSION['wsEditToken'] ) ) {
02853 $token = self::generateToken();
02854 $_SESSION['wsEditToken'] = $token;
02855 } else {
02856 $token = $_SESSION['wsEditToken'];
02857 }
02858 if( is_array( $salt ) ) {
02859 $salt = implode( '|', $salt );
02860 }
02861 return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX;
02862 }
02863 }
02864
02871 public static function generateToken( $salt = '' ) {
02872 $token = dechex( mt_rand() ) . dechex( mt_rand() );
02873 return md5( $token . $salt );
02874 }
02875
02886 function matchEditToken( $val, $salt = '' ) {
02887 $sessionToken = $this->editToken( $salt );
02888 if ( $val != $sessionToken ) {
02889 wfDebug( "User::matchEditToken: broken session data\n" );
02890 }
02891 return $val == $sessionToken;
02892 }
02893
02902 function matchEditTokenNoSuffix( $val, $salt = '' ) {
02903 $sessionToken = $this->editToken( $salt );
02904 return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
02905 }
02906
02913 function sendConfirmationMail() {
02914 global $wgLang;
02915 $expiration = null;
02916 $token = $this->confirmationToken( $expiration );
02917 $url = $this->confirmationTokenUrl( $token );
02918 $invalidateURL = $this->invalidationTokenUrl( $token );
02919 $this->saveSettings();
02920
02921 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
02922 wfMsg( 'confirmemail_body',
02923 wfGetIP(),
02924 $this->getName(),
02925 $url,
02926 $wgLang->timeanddate( $expiration, false ),
02927 $invalidateURL,
02928 $wgLang->date( $expiration, false ),
02929 $wgLang->time( $expiration, false ) ) );
02930 }
02931
02942 function sendMail( $subject, $body, $from = null, $replyto = null ) {
02943 if( is_null( $from ) ) {
02944 global $wgPasswordSender;
02945 $from = $wgPasswordSender;
02946 }
02947
02948 $to = new MailAddress( $this );
02949 $sender = new MailAddress( $from );
02950 return UserMailer::send( $to, $sender, $subject, $body, $replyto );
02951 }
02952
02964 function confirmationToken( &$expiration ) {
02965 $now = time();
02966 $expires = $now + 7 * 24 * 60 * 60;
02967 $expiration = wfTimestamp( TS_MW, $expires );
02968 $token = self::generateToken( $this->mId . $this->mEmail . $expires );
02969 $hash = md5( $token );
02970 $this->load();
02971 $this->mEmailToken = $hash;
02972 $this->mEmailTokenExpires = $expiration;
02973 return $token;
02974 }
02975
02982 function confirmationTokenUrl( $token ) {
02983 return $this->getTokenUrl( 'ConfirmEmail', $token );
02984 }
02985
02992 function invalidationTokenUrl( $token ) {
02993 return $this->getTokenUrl( 'Invalidateemail', $token );
02994 }
02995
03010 protected function getTokenUrl( $page, $token ) {
03011 global $wgArticlePath;
03012 return wfExpandUrl(
03013 str_replace(
03014 '$1',
03015 "Special:$page/$token",
03016 $wgArticlePath ) );
03017 }
03018
03024 function confirmEmail() {
03025 $this->setEmailAuthenticationTimestamp( wfTimestampNow() );
03026 wfRunHooks( 'ConfirmEmailComplete', array( $this ) );
03027 return true;
03028 }
03029
03036 function invalidateEmail() {
03037 $this->load();
03038 $this->mEmailToken = null;
03039 $this->mEmailTokenExpires = null;
03040 $this->setEmailAuthenticationTimestamp( null );
03041 wfRunHooks( 'InvalidateEmailComplete', array( $this ) );
03042 return true;
03043 }
03044
03049 function setEmailAuthenticationTimestamp( $timestamp ) {
03050 $this->load();
03051 $this->mEmailAuthenticated = $timestamp;
03052 wfRunHooks( 'UserSetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
03053 }
03054
03060 function canSendEmail() {
03061 global $wgEnableEmail, $wgEnableUserEmail;
03062 if( !$wgEnableEmail || !$wgEnableUserEmail || !$this->isAllowed( 'sendemail' ) ) {
03063 return false;
03064 }
03065 $canSend = $this->isEmailConfirmed();
03066 wfRunHooks( 'UserCanSendEmail', array( &$this, &$canSend ) );
03067 return $canSend;
03068 }
03069
03075 function canReceiveEmail() {
03076 return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
03077 }
03078
03089 function isEmailConfirmed() {
03090 global $wgEmailAuthentication;
03091 $this->load();
03092 $confirmed = true;
03093 if( wfRunHooks( 'EmailConfirmed', array( &$this, &$confirmed ) ) ) {
03094 if( $this->isAnon() )
03095 return false;
03096 if( !self::isValidEmailAddr( $this->mEmail ) )
03097 return false;
03098 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
03099 return false;
03100 return true;
03101 } else {
03102 return $confirmed;
03103 }
03104 }
03105
03110 function isEmailConfirmationPending() {
03111 global $wgEmailAuthentication;
03112 return $wgEmailAuthentication &&
03113 !$this->isEmailConfirmed() &&
03114 $this->mEmailToken &&
03115 $this->mEmailTokenExpires > wfTimestamp();
03116 }
03117
03124 public function getRegistration() {
03125 return $this->getId() > 0
03126 ? $this->mRegistration
03127 : false;
03128 }
03129
03136 public function getFirstEditTimestamp() {
03137 if( $this->getId() == 0 ) return false;
03138 $dbr = wfGetDB( DB_SLAVE );
03139 $time = $dbr->selectField( 'revision', 'rev_timestamp',
03140 array( 'rev_user' => $this->getId() ),
03141 __METHOD__,
03142 array( 'ORDER BY' => 'rev_timestamp ASC' )
03143 );
03144 if( !$time ) return false;
03145 return wfTimestamp( TS_MW, $time );
03146 }
03147
03154 static function getGroupPermissions( $groups ) {
03155 global $wgGroupPermissions, $wgRevokePermissions;
03156 $rights = array();
03157
03158 foreach( $groups as $group ) {
03159 if( isset( $wgGroupPermissions[$group] ) ) {
03160 $rights = array_merge( $rights,
03161
03162 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
03163 }
03164 }
03165
03166 foreach( $groups as $group ) {
03167 if( isset( $wgRevokePermissions[$group] ) ) {
03168 $rights = array_diff( $rights,
03169 array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
03170 }
03171 }
03172 return array_unique( $rights );
03173 }
03174
03181 static function getGroupsWithPermission( $role ) {
03182 global $wgGroupPermissions;
03183 $allowedGroups = array();
03184 foreach ( $wgGroupPermissions as $group => $rights ) {
03185 if ( isset( $rights[$role] ) && $rights[$role] ) {
03186 $allowedGroups[] = $group;
03187 }
03188 }
03189 return $allowedGroups;
03190 }
03191
03198 static function getGroupName( $group ) {
03199 global $wgMessageCache;
03200 $wgMessageCache->loadAllMessages();
03201 $key = "group-$group";
03202 $name = wfMsg( $key );
03203 return $name == '' || wfEmptyMsg( $key, $name )
03204 ? $group
03205 : $name;
03206 }
03207
03214 static function getGroupMember( $group ) {
03215 global $wgMessageCache;
03216 $wgMessageCache->loadAllMessages();
03217 $key = "group-$group-member";
03218 $name = wfMsg( $key );
03219 return $name == '' || wfEmptyMsg( $key, $name )
03220 ? $group
03221 : $name;
03222 }
03223
03230 static function getAllGroups() {
03231 global $wgGroupPermissions, $wgRevokePermissions;
03232 return array_diff(
03233 array_merge( array_keys( $wgGroupPermissions ), array_keys( $wgRevokePermissions ) ),
03234 self::getImplicitGroups()
03235 );
03236 }
03237
03242 static function getAllRights() {
03243 if ( self::$mAllRights === false ) {
03244 global $wgAvailableRights;
03245 if ( count( $wgAvailableRights ) ) {
03246 self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
03247 } else {
03248 self::$mAllRights = self::$mCoreRights;
03249 }
03250 wfRunHooks( 'UserGetAllRights', array( &self::$mAllRights ) );
03251 }
03252 return self::$mAllRights;
03253 }
03254
03259 public static function getImplicitGroups() {
03260 global $wgImplicitGroups;
03261 $groups = $wgImplicitGroups;
03262 wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) ); #deprecated, use $wgImplictGroups instead
03263 return $groups;
03264 }
03265
03272 static function getGroupPage( $group ) {
03273 global $wgMessageCache;
03274 $wgMessageCache->loadAllMessages();
03275 $page = wfMsgForContent( 'grouppage-' . $group );
03276 if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) {
03277 $title = Title::newFromText( $page );
03278 if( is_object( $title ) )
03279 return $title;
03280 }
03281 return false;
03282 }
03283
03292 static function makeGroupLinkHTML( $group, $text = '' ) {
03293 if( $text == '' ) {
03294 $text = self::getGroupName( $group );
03295 }
03296 $title = self::getGroupPage( $group );
03297 if( $title ) {
03298 global $wgUser;
03299 $sk = $wgUser->getSkin();
03300 return $sk->link( $title, htmlspecialchars( $text ) );
03301 } else {
03302 return $text;
03303 }
03304 }
03305
03314 static function makeGroupLinkWiki( $group, $text = '' ) {
03315 if( $text == '' ) {
03316 $text = self::getGroupName( $group );
03317 }
03318 $title = self::getGroupPage( $group );
03319 if( $title ) {
03320 $page = $title->getPrefixedText();
03321 return "[[$page|$text]]";
03322 } else {
03323 return $text;
03324 }
03325 }
03326
03336 static function changeableByGroup( $group ) {
03337 global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
03338
03339 $groups = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
03340 if( empty( $wgAddGroups[$group] ) ) {
03341
03342 } elseif( $wgAddGroups[$group] === true ) {
03343
03344 $groups['add'] = self::getAllGroups();
03345 } elseif( is_array( $wgAddGroups[$group] ) ) {
03346 $groups['add'] = $wgAddGroups[$group];
03347 }
03348
03349
03350 if( empty( $wgRemoveGroups[$group] ) ) {
03351 } elseif( $wgRemoveGroups[$group] === true ) {
03352 $groups['remove'] = self::getAllGroups();
03353 } elseif( is_array( $wgRemoveGroups[$group] ) ) {
03354 $groups['remove'] = $wgRemoveGroups[$group];
03355 }
03356
03357
03358 if( empty( $wgGroupsAddToSelf['user']) || $wgGroupsAddToSelf['user'] !== true ) {
03359 foreach( $wgGroupsAddToSelf as $key => $value ) {
03360 if( is_int( $key ) ) {
03361 $wgGroupsAddToSelf['user'][] = $value;
03362 }
03363 }
03364 }
03365
03366 if( empty( $wgGroupsRemoveFromSelf['user']) || $wgGroupsRemoveFromSelf['user'] !== true ) {
03367 foreach( $wgGroupsRemoveFromSelf as $key => $value ) {
03368 if( is_int( $key ) ) {
03369 $wgGroupsRemoveFromSelf['user'][] = $value;
03370 }
03371 }
03372 }
03373
03374
03375 if( empty( $wgGroupsAddToSelf[$group] ) ) {
03376 } elseif( $wgGroupsAddToSelf[$group] === true ) {
03377
03378 $groups['add-self'] = User::getAllGroups();
03379 } elseif( is_array( $wgGroupsAddToSelf[$group] ) ) {
03380 $groups['add-self'] = $wgGroupsAddToSelf[$group];
03381 }
03382
03383 if( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
03384 } elseif( $wgGroupsRemoveFromSelf[$group] === true ) {
03385 $groups['remove-self'] = User::getAllGroups();
03386 } elseif( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
03387 $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
03388 }
03389
03390 return $groups;
03391 }
03392
03400 function changeableGroups() {
03401 if( $this->isAllowed( 'userrights' ) ) {
03402
03403
03404
03405
03406 $all = array_merge( User::getAllGroups() );
03407 return array(
03408 'add' => $all,
03409 'remove' => $all,
03410 'add-self' => array(),
03411 'remove-self' => array()
03412 );
03413 }
03414
03415
03416 $groups = array(
03417 'add' => array(),
03418 'remove' => array(),
03419 'add-self' => array(),
03420 'remove-self' => array()
03421 );
03422 $addergroups = $this->getEffectiveGroups();
03423
03424 foreach( $addergroups as $addergroup ) {
03425 $groups = array_merge_recursive(
03426 $groups, $this->changeableByGroup( $addergroup )
03427 );
03428 $groups['add'] = array_unique( $groups['add'] );
03429 $groups['remove'] = array_unique( $groups['remove'] );
03430 $groups['add-self'] = array_unique( $groups['add-self'] );
03431 $groups['remove-self'] = array_unique( $groups['remove-self'] );
03432 }
03433 return $groups;
03434 }
03435
03440 function incEditCount() {
03441 if( !$this->isAnon() ) {
03442 $dbw = wfGetDB( DB_MASTER );
03443 $dbw->update( 'user',
03444 array( 'user_editcount=user_editcount+1' ),
03445 array( 'user_id' => $this->getId() ),
03446 __METHOD__ );
03447
03448
03449 if( $dbw->affectedRows() == 0 ) {
03450
03451
03452 $dbr = wfGetDB( DB_SLAVE );
03453 $count = $dbr->selectField( 'revision',
03454 'COUNT(rev_user)',
03455 array( 'rev_user' => $this->getId() ),
03456 __METHOD__ );
03457
03458
03459 if( $dbr !== $dbw ) {
03460
03461
03462
03463 $count++;
03464 } else {
03465
03466
03467
03468 }
03469
03470 $dbw->update( 'user',
03471 array( 'user_editcount' => $count ),
03472 array( 'user_id' => $this->getId() ),
03473 __METHOD__ );
03474 }
03475 }
03476
03477 $this->invalidateCache();
03478 }
03479
03486 static function getRightDescription( $right ) {
03487 global $wgMessageCache;
03488 $wgMessageCache->loadAllMessages();
03489 $key = "right-$right";
03490 $name = wfMsg( $key );
03491 return $name == '' || wfEmptyMsg( $key, $name )
03492 ? $right
03493 : $name;
03494 }
03495
03503 static function oldCrypt( $password, $userId ) {
03504 global $wgPasswordSalt;
03505 if ( $wgPasswordSalt ) {
03506 return md5( $userId . '-' . md5( $password ) );
03507 } else {
03508 return md5( $password );
03509 }
03510 }
03511
03520 static function crypt( $password, $salt = false ) {
03521 global $wgPasswordSalt;
03522
03523 $hash = '';
03524 if( !wfRunHooks( 'UserCryptPassword', array( &$password, &$salt, &$wgPasswordSalt, &$hash ) ) ) {
03525 return $hash;
03526 }
03527
03528 if( $wgPasswordSalt ) {
03529 if ( $salt === false ) {
03530 $salt = substr( wfGenerateToken(), 0, 8 );
03531 }
03532 return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
03533 } else {
03534 return ':A:' . md5( $password );
03535 }
03536 }
03537
03547 static function comparePasswords( $hash, $password, $userId = false ) {
03548 $m = false;
03549 $type = substr( $hash, 0, 3 );
03550
03551 $result = false;
03552 if( !wfRunHooks( 'UserComparePasswords', array( &$hash, &$password, &$userId, &$result ) ) ) {
03553 return $result;
03554 }
03555
03556 if ( $type == ':A:' ) {
03557 # Unsalted
03558 return md5( $password ) === substr( $hash, 3 );
03559 } elseif ( $type == ':B:' ) {
03560 # Salted
03561 list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
03562 return md5( $salt.'-'.md5( $password ) ) == $realHash;
03563 } else {
03564 # Old-style
03565 return self::oldCrypt( $password, $userId ) === $hash;
03566 }
03567 }
03568
03573 public function addNewUserLogEntry( $byEmail = false ) {
03574 global $wgUser, $wgNewUserLog;
03575 if( empty( $wgNewUserLog ) ) {
03576 return true;
03577 }
03578
03579 if( $this->getName() == $wgUser->getName() ) {
03580 $action = 'create';
03581 $message = '';
03582 } else {
03583 $action = 'create2';
03584 $message = $byEmail
03585 ? wfMsgForContent( 'newuserlog-byemail' )
03586 : '';
03587 }
03588 $log = new LogPage( 'newusers' );
03589 $log->addEntry(
03590 $action,
03591 $this->getUserPage(),
03592 $message,
03593 array( $this->getId() )
03594 );
03595 return true;
03596 }
03597
03602 public function addNewUserLogEntryAutoCreate() {
03603 global $wgNewUserLog;
03604 if( empty( $wgNewUserLog ) ) {
03605 return true;
03606 }
03607 $log = new LogPage( 'newusers', false );
03608 $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) );
03609 return true;
03610 }
03611
03612 protected function loadOptions() {
03613 $this->load();
03614 if ( $this->mOptionsLoaded || !$this->getId() )
03615 return;
03616
03617 $this->mOptions = self::getDefaultOptions();
03618
03619
03620 if ( !is_null( $this->mOptionOverrides ) ) {
03621 wfDebug( "Loading options for user " . $this->getId() . " from override cache.\n" );
03622 foreach( $this->mOptionOverrides as $key => $value ) {
03623 $this->mOptions[$key] = $value;
03624 }
03625 } else {
03626 wfDebug( "Loading options for user " . $this->getId() . " from database.\n" );
03627
03628 $dbr = wfGetDB( DB_SLAVE );
03629
03630 $res = $dbr->select(
03631 'user_properties',
03632 '*',
03633 array( 'up_user' => $this->getId() ),
03634 __METHOD__
03635 );
03636
03637 while( $row = $dbr->fetchObject( $res ) ) {
03638 $this->mOptionOverrides[$row->up_property] = $row->up_value;
03639 $this->mOptions[$row->up_property] = $row->up_value;
03640 }
03641 }
03642
03643 $this->mOptionsLoaded = true;
03644
03645 wfRunHooks( 'UserLoadOptions', array( $this, &$this->mOptions ) );
03646 }
03647
03648 protected function saveOptions() {
03649 global $wgAllowPrefChange;
03650
03651 $extuser = ExternalUser::newFromUser( $this );
03652
03653 $this->loadOptions();
03654 $dbw = wfGetDB( DB_MASTER );
03655
03656 $insert_rows = array();
03657
03658 $saveOptions = $this->mOptions;
03659
03660
03661
03662 if( !wfRunHooks( 'UserSaveOptions', array( $this, &$saveOptions ) ) )
03663 return;
03664
03665 foreach( $saveOptions as $key => $value ) {
03666 # Don't bother storing default values
03667 if ( ( is_null( self::getDefaultOption( $key ) ) &&
03668 !( $value === false || is_null($value) ) ) ||
03669 $value != self::getDefaultOption( $key ) ) {
03670 $insert_rows[] = array(
03671 'up_user' => $this->getId(),
03672 'up_property' => $key,
03673 'up_value' => $value,
03674 );
03675 }
03676 if ( $extuser && isset( $wgAllowPrefChange[$key] ) ) {
03677 switch ( $wgAllowPrefChange[$key] ) {
03678 case 'local':
03679 case 'message':
03680 break;
03681 case 'semiglobal':
03682 case 'global':
03683 $extuser->setPref( $key, $value );
03684 }
03685 }
03686 }
03687
03688 $dbw->begin();
03689 $dbw->delete( 'user_properties', array( 'up_user' => $this->getId() ), __METHOD__ );
03690 $dbw->insert( 'user_properties', $insert_rows, __METHOD__ );
03691 $dbw->commit();
03692 }
03693
03712 public static function passwordChangeInputAttribs() {
03713 global $wgMinimalPasswordLength;
03714
03715 if ( $wgMinimalPasswordLength == 0 ) {
03716 return array();
03717 }
03718
03719 # Note that the pattern requirement will always be satisfied if the
03720 # input is empty, so we need required in all cases.
03721 $ret = array( 'required' );
03722
03723 # We can't actually do this right now, because Opera 9.6 will print out
03724 # the entered password visibly in its error message! When other
03725 # browsers add support for this attribute, or Opera fixes its support,
03726 # we can add support with a version check to avoid doing this on Opera
03727 # versions where it will be a problem. Reported to Opera as
03728 # DSK-262266, but they don't have a public bug tracker for us to follow.
03729
03730
03731
03732
03733
03734
03735
03736
03737 return $ret;
03738 }
03739 }