00001 <?php
00011 define( 'USER_TOKEN_LENGTH', 32 );
00012
00017 define( 'MW_USER_VERSION', 6 );
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 'mOptions',
00113 'mTouched',
00114 'mToken',
00115 'mEmailAuthenticated',
00116 'mEmailToken',
00117 'mEmailTokenExpires',
00118 'mRegistration',
00119 'mEditCount',
00120
00121 'mGroups',
00122 );
00123
00130 static $mCoreRights = array(
00131 'apihighlimits',
00132 'autoconfirmed',
00133 'autopatrol',
00134 'bigdelete',
00135 'block',
00136 'blockemail',
00137 'bot',
00138 'browsearchive',
00139 'createaccount',
00140 'createpage',
00141 'createtalk',
00142 'delete',
00143 'deletedhistory',
00144 'deleterevision',
00145 'edit',
00146 'editinterface',
00147 'editusercssjs',
00148 'hideuser',
00149 'import',
00150 'importupload',
00151 'ipblock-exempt',
00152 'markbotedits',
00153 'minoredit',
00154 'move',
00155 'movefile',
00156 'move-rootuserpages',
00157 'move-subpages',
00158 'nominornewtalk',
00159 'noratelimit',
00160 'override-export-depth',
00161 'patrol',
00162 'protect',
00163 'proxyunbannable',
00164 'purge',
00165 'read',
00166 'reupload',
00167 'reupload-shared',
00168 'rollback',
00169 'siteadmin',
00170 'suppressionlog',
00171 'suppressredirect',
00172 'suppressrevision',
00173 'trackback',
00174 'undelete',
00175 'unwatchedpages',
00176 'upload',
00177 'upload_by_url',
00178 'userrights',
00179 'userrights-interwiki',
00180 'writeapi',
00181 );
00185 static $mAllRights = false;
00186
00189 var $mId, $mName, $mRealName, $mPassword, $mNewpassword, $mNewpassTime,
00190 $mEmail, $mOptions, $mTouched, $mToken, $mEmailAuthenticated,
00191 $mEmailToken, $mEmailTokenExpires, $mRegistration, $mGroups;
00193
00197 var $mDataLoaded, $mAuthLoaded;
00198
00208 var $mFrom;
00209
00212 var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mSkin, $mRights,
00213 $mBlockreason, $mBlock, $mEffectiveGroups, $mBlockedGlobally,
00214 $mLocked, $mHideName;
00216
00227 function User() {
00228 $this->clearInstanceCache( 'defaults' );
00229 }
00230
00234 function load() {
00235 if ( $this->mDataLoaded ) {
00236 return;
00237 }
00238 wfProfileIn( __METHOD__ );
00239
00240 # Set it now to avoid infinite recursion in accessors
00241 $this->mDataLoaded = true;
00242
00243 switch ( $this->mFrom ) {
00244 case 'defaults':
00245 $this->loadDefaults();
00246 break;
00247 case 'name':
00248 $this->mId = self::idFromName( $this->mName );
00249 if ( !$this->mId ) {
00250 # Nonexistent user placeholder object
00251 $this->loadDefaults( $this->mName );
00252 } else {
00253 $this->loadFromId();
00254 }
00255 break;
00256 case 'id':
00257 $this->loadFromId();
00258 break;
00259 case 'session':
00260 $this->loadFromSession();
00261 wfRunHooks( 'UserLoadAfterLoadFromSession', array( $this ) );
00262 break;
00263 default:
00264 throw new MWException( "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
00265 }
00266 wfProfileOut( __METHOD__ );
00267 }
00268
00274 function loadFromId() {
00275 global $wgMemc;
00276 if ( $this->mId == 0 ) {
00277 $this->loadDefaults();
00278 return false;
00279 }
00280
00281 # Try cache
00282 $key = wfMemcKey( 'user', 'id', $this->mId );
00283 $data = $wgMemc->get( $key );
00284 if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
00285 # Object is expired, load from DB
00286 $data = false;
00287 }
00288
00289 if ( !$data ) {
00290 wfDebug( "Cache miss for user {$this->mId}\n" );
00291 # Load from DB
00292 if ( !$this->loadFromDatabase() ) {
00293 # Can't load from ID, user is anonymous
00294 return false;
00295 }
00296 $this->saveToCache();
00297 } else {
00298 wfDebug( "Got user {$this->mId} from cache\n" );
00299 # Restore from cache
00300 foreach ( self::$mCacheVars as $name ) {
00301 $this->$name = $data[$name];
00302 }
00303 }
00304 return true;
00305 }
00306
00310 function saveToCache() {
00311 $this->load();
00312 $this->loadGroups();
00313 if ( $this->isAnon() ) {
00314
00315 return;
00316 }
00317 $data = array();
00318 foreach ( self::$mCacheVars as $name ) {
00319 $data[$name] = $this->$name;
00320 }
00321 $data['mVersion'] = MW_USER_VERSION;
00322 $key = wfMemcKey( 'user', 'id', $this->mId );
00323 global $wgMemc;
00324 $wgMemc->set( $key, $data );
00325 }
00326
00327
00330
00346 static function newFromName( $name, $validate = 'valid' ) {
00347 if ( $validate === true ) {
00348 $validate = 'valid';
00349 }
00350 $name = self::getCanonicalName( $name, $validate );
00351 if ( $name === false ) {
00352 return null;
00353 } else {
00354 # Create unloaded user object
00355 $u = new User;
00356 $u->mName = $name;
00357 $u->mFrom = 'name';
00358 return $u;
00359 }
00360 }
00361
00368 static function newFromId( $id ) {
00369 $u = new User;
00370 $u->mId = $id;
00371 $u->mFrom = 'id';
00372 return $u;
00373 }
00374
00385 static function newFromConfirmationCode( $code ) {
00386 $dbr = wfGetDB( DB_SLAVE );
00387 $id = $dbr->selectField( 'user', 'user_id', array(
00388 'user_email_token' => md5( $code ),
00389 'user_email_token_expires > ' . $dbr->addQuotes( $dbr->timestamp() ),
00390 ) );
00391 if( $id !== false ) {
00392 return User::newFromId( $id );
00393 } else {
00394 return null;
00395 }
00396 }
00397
00404 static function newFromSession() {
00405 $user = new User;
00406 $user->mFrom = 'session';
00407 return $user;
00408 }
00409
00416 static function newFromRow( $row ) {
00417 $user = new User;
00418 $user->loadFromRow( $row );
00419 return $user;
00420 }
00421
00423
00424
00430 static function whoIs( $id ) {
00431 $dbr = wfGetDB( DB_SLAVE );
00432 return $dbr->selectField( 'user', 'user_name', array( 'user_id' => $id ), 'User::whoIs' );
00433 }
00434
00441 static function whoIsReal( $id ) {
00442 $dbr = wfGetDB( DB_SLAVE );
00443 return $dbr->selectField( 'user', 'user_real_name', array( 'user_id' => $id ), __METHOD__ );
00444 }
00445
00451 static function idFromName( $name ) {
00452 $nt = Title::makeTitleSafe( NS_USER, $name );
00453 if( is_null( $nt ) ) {
00454 # Illegal name
00455 return null;
00456 }
00457 $dbr = wfGetDB( DB_SLAVE );
00458 $s = $dbr->selectRow( 'user', array( 'user_id' ), array( 'user_name' => $nt->getText() ), __METHOD__ );
00459
00460 if ( $s === false ) {
00461 return 0;
00462 } else {
00463 return $s->user_id;
00464 }
00465 }
00466
00483 static function isIP( $name ) {
00484 return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name) || IP::isIPv6($name);
00485 }
00486
00498 static function isValidUserName( $name ) {
00499 global $wgContLang, $wgMaxNameChars;
00500
00501 if ( $name == ''
00502 || User::isIP( $name )
00503 || strpos( $name, '/' ) !== false
00504 || strlen( $name ) > $wgMaxNameChars
00505 || $name != $wgContLang->ucfirst( $name ) ) {
00506 wfDebugLog( 'username', __METHOD__ .
00507 ": '$name' invalid due to empty, IP, slash, length, or lowercase" );
00508 return false;
00509 }
00510
00511
00512
00513 $parsed = Title::newFromText( $name );
00514 if( is_null( $parsed )
00515 || $parsed->getNamespace()
00516 || strcmp( $name, $parsed->getPrefixedText() ) ) {
00517 wfDebugLog( 'username', __METHOD__ .
00518 ": '$name' invalid due to ambiguous prefixes" );
00519 return false;
00520 }
00521
00522
00523
00524 $unicodeBlacklist = '/[' .
00525 '\x{0080}-\x{009f}' . # iso-8859-1 control chars
00526 '\x{00a0}' . # non-breaking space
00527 '\x{2000}-\x{200f}' . # various whitespace
00528 '\x{2028}-\x{202f}' . # breaks and control chars
00529 '\x{3000}' . # ideographic space
00530 '\x{e000}-\x{f8ff}' . # private use
00531 ']/u';
00532 if( preg_match( $unicodeBlacklist, $name ) ) {
00533 wfDebugLog( 'username', __METHOD__ .
00534 ": '$name' invalid due to blacklisted characters" );
00535 return false;
00536 }
00537
00538 return true;
00539 }
00540
00552 static function isUsableName( $name ) {
00553 global $wgReservedUsernames;
00554
00555 if ( !self::isValidUserName( $name ) ) {
00556 return false;
00557 }
00558
00559 static $reservedUsernames = false;
00560 if ( !$reservedUsernames ) {
00561 $reservedUsernames = $wgReservedUsernames;
00562 wfRunHooks( 'UserGetReservedNames', array( &$reservedUsernames ) );
00563 }
00564
00565
00566 foreach ( $reservedUsernames as $reserved ) {
00567 if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
00568 $reserved = wfMsgForContent( substr( $reserved, 4 ) );
00569 }
00570 if ( $reserved == $name ) {
00571 return false;
00572 }
00573 }
00574 return true;
00575 }
00576
00590 static function isCreatableName( $name ) {
00591 global $wgInvalidUsernameCharacters;
00592 return
00593 self::isUsableName( $name ) &&
00594
00595
00596 !preg_match( '/[' . preg_quote( $wgInvalidUsernameCharacters, '/' ) . ']/', $name );
00597 }
00598
00605 function isValidPassword( $password ) {
00606 global $wgMinimalPasswordLength, $wgContLang;
00607
00608 $result = null;
00609 if( !wfRunHooks( 'isValidPassword', array( $password, &$result, $this ) ) )
00610 return $result;
00611 if( $result === false )
00612 return false;
00613
00614
00615 return strlen( $password ) >= $wgMinimalPasswordLength
00616 && $wgContLang->lc( $password ) !== $wgContLang->lc( $this->mName );
00617 }
00618
00631 public static function isValidEmailAddr( $addr ) {
00632 $result = null;
00633 if( !wfRunHooks( 'isValidEmailAddr', array( $addr, &$result ) ) ) {
00634 return $result;
00635 }
00636
00637 return strpos( $addr, '@' ) !== false;
00638 }
00639
00650 static function getCanonicalName( $name, $validate = 'valid' ) {
00651 # Force usernames to capital
00652 global $wgContLang;
00653 $name = $wgContLang->ucfirst( $name );
00654
00655 # Reject names containing '#'; these will be cleaned up
00656 # with title normalisation, but then it's too late to
00657 # check elsewhere
00658 if( strpos( $name, '#' ) !== false )
00659 return false;
00660
00661 # Clean up name according to title rules
00662 $t = ($validate === 'valid') ?
00663 Title::newFromText( $name ) : Title::makeTitle( NS_USER, $name );
00664 # Check for invalid titles
00665 if( is_null( $t ) ) {
00666 return false;
00667 }
00668
00669 # Reject various classes of invalid names
00670 $name = $t->getText();
00671 global $wgAuth;
00672 $name = $wgAuth->getCanonicalName( $t->getText() );
00673
00674 switch ( $validate ) {
00675 case false:
00676 break;
00677 case 'valid':
00678 if ( !User::isValidUserName( $name ) ) {
00679 $name = false;
00680 }
00681 break;
00682 case 'usable':
00683 if ( !User::isUsableName( $name ) ) {
00684 $name = false;
00685 }
00686 break;
00687 case 'creatable':
00688 if ( !User::isCreatableName( $name ) ) {
00689 $name = false;
00690 }
00691 break;
00692 default:
00693 throw new MWException( 'Invalid parameter value for $validate in '.__METHOD__ );
00694 }
00695 return $name;
00696 }
00697
00705 static function edits( $uid ) {
00706 wfProfileIn( __METHOD__ );
00707 $dbr = wfGetDB( DB_SLAVE );
00708
00709 $field = $dbr->selectField(
00710 'user', 'user_editcount',
00711 array( 'user_id' => $uid ),
00712 __METHOD__
00713 );
00714
00715 if( $field === null ) {
00716 $dbw = wfGetDB( DB_MASTER );
00717 $count = $dbr->selectField(
00718 'revision', 'count(*)',
00719 array( 'rev_user' => $uid ),
00720 __METHOD__
00721 );
00722 $dbw->update(
00723 'user',
00724 array( 'user_editcount' => $count ),
00725 array( 'user_id' => $uid ),
00726 __METHOD__
00727 );
00728 } else {
00729 $count = $field;
00730 }
00731 wfProfileOut( __METHOD__ );
00732 return $count;
00733 }
00734
00741 static function randomPassword() {
00742 global $wgMinimalPasswordLength;
00743 $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
00744 $l = strlen( $pwchars ) - 1;
00745
00746 $pwlength = max( 7, $wgMinimalPasswordLength );
00747 $digit = mt_rand(0, $pwlength - 1);
00748 $np = '';
00749 for ( $i = 0; $i < $pwlength; $i++ ) {
00750 $np .= $i == $digit ? chr( mt_rand(48, 57) ) : $pwchars{ mt_rand(0, $l)};
00751 }
00752 return $np;
00753 }
00754
00762 function loadDefaults( $name = false ) {
00763 wfProfileIn( __METHOD__ );
00764
00765 global $wgCookiePrefix;
00766
00767 $this->mId = 0;
00768 $this->mName = $name;
00769 $this->mRealName = '';
00770 $this->mPassword = $this->mNewpassword = '';
00771 $this->mNewpassTime = null;
00772 $this->mEmail = '';
00773 $this->mOptions = null; # Defer init
00774
00775 if ( isset( $_COOKIE[$wgCookiePrefix.'LoggedOut'] ) ) {
00776 $this->mTouched = wfTimestamp( TS_MW, $_COOKIE[$wgCookiePrefix.'LoggedOut'] );
00777 } else {
00778 $this->mTouched = '0'; # Allow any pages to be cached
00779 }
00780
00781 $this->setToken(); # Random
00782 $this->mEmailAuthenticated = null;
00783 $this->mEmailToken = '';
00784 $this->mEmailTokenExpires = null;
00785 $this->mRegistration = wfTimestamp( TS_MW );
00786 $this->mGroups = array();
00787
00788 wfRunHooks( 'UserLoadDefaults', array( $this, $name ) );
00789
00790 wfProfileOut( __METHOD__ );
00791 }
00792
00796 function SetupSession() {
00797 wfDeprecated( __METHOD__ );
00798 wfSetupSession();
00799 }
00800
00806 private function loadFromSession() {
00807 global $wgMemc, $wgCookiePrefix;
00808
00809 $result = null;
00810 wfRunHooks( 'UserLoadFromSession', array( $this, &$result ) );
00811 if ( $result !== null ) {
00812 return $result;
00813 }
00814
00815 if ( isset( $_COOKIE["{$wgCookiePrefix}UserID"] ) ) {
00816 $sId = intval( $_COOKIE["{$wgCookiePrefix}UserID"] );
00817 if( isset( $_SESSION['wsUserID'] ) && $sId != $_SESSION['wsUserID'] ) {
00818 $this->loadDefaults();
00819 wfDebugLog( 'loginSessions', "Session user ID ({$_SESSION['wsUserID']}) and
00820 cookie user ID ($sId) don't match!" );
00821 return false;
00822 }
00823 $_SESSION['wsUserID'] = $sId;
00824 } else if ( isset( $_SESSION['wsUserID'] ) ) {
00825 if ( $_SESSION['wsUserID'] != 0 ) {
00826 $sId = $_SESSION['wsUserID'];
00827 } else {
00828 $this->loadDefaults();
00829 return false;
00830 }
00831 } else {
00832 $this->loadDefaults();
00833 return false;
00834 }
00835
00836 if ( isset( $_SESSION['wsUserName'] ) ) {
00837 $sName = $_SESSION['wsUserName'];
00838 } else if ( isset( $_COOKIE["{$wgCookiePrefix}UserName"] ) ) {
00839 $sName = $_COOKIE["{$wgCookiePrefix}UserName"];
00840 $_SESSION['wsUserName'] = $sName;
00841 } else {
00842 $this->loadDefaults();
00843 return false;
00844 }
00845
00846 $passwordCorrect = FALSE;
00847 $this->mId = $sId;
00848 if ( !$this->loadFromId() ) {
00849 # Not a valid ID, loadFromId has switched the object to anon for us
00850 return false;
00851 }
00852
00853 if ( isset( $_SESSION['wsToken'] ) ) {
00854 $passwordCorrect = $_SESSION['wsToken'] == $this->mToken;
00855 $from = 'session';
00856 } else if ( isset( $_COOKIE["{$wgCookiePrefix}Token"] ) ) {
00857 $passwordCorrect = $this->mToken == $_COOKIE["{$wgCookiePrefix}Token"];
00858 $from = 'cookie';
00859 } else {
00860 # No session or persistent login cookie
00861 $this->loadDefaults();
00862 return false;
00863 }
00864
00865 if ( ( $sName == $this->mName ) && $passwordCorrect ) {
00866 $_SESSION['wsToken'] = $this->mToken;
00867 wfDebug( "Logged in from $from\n" );
00868 return true;
00869 } else {
00870 # Invalid credentials
00871 wfDebug( "Can't log in from $from, invalid credentials\n" );
00872 $this->loadDefaults();
00873 return false;
00874 }
00875 }
00876
00884 function loadFromDatabase() {
00885 # Paranoia
00886 $this->mId = intval( $this->mId );
00887
00889 if( !$this->mId ) {
00890 $this->loadDefaults();
00891 return false;
00892 }
00893
00894 $dbr = wfGetDB( DB_MASTER );
00895 $s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
00896
00897 wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
00898
00899 if ( $s !== false ) {
00900 # Initialise user table data
00901 $this->loadFromRow( $s );
00902 $this->mGroups = null;
00903 $this->getEditCount();
00904 return true;
00905 } else {
00906 # Invalid user_id
00907 $this->mId = 0;
00908 $this->loadDefaults();
00909 return false;
00910 }
00911 }
00912
00918 function loadFromRow( $row ) {
00919 $this->mDataLoaded = true;
00920
00921 if ( isset( $row->user_id ) ) {
00922 $this->mId = intval( $row->user_id );
00923 }
00924 $this->mName = $row->user_name;
00925 $this->mRealName = $row->user_real_name;
00926 $this->mPassword = $row->user_password;
00927 $this->mNewpassword = $row->user_newpassword;
00928 $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time );
00929 $this->mEmail = $row->user_email;
00930 $this->decodeOptions( $row->user_options );
00931 $this->mTouched = wfTimestamp(TS_MW,$row->user_touched);
00932 $this->mToken = $row->user_token;
00933 $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
00934 $this->mEmailToken = $row->user_email_token;
00935 $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
00936 $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration );
00937 $this->mEditCount = $row->user_editcount;
00938 }
00939
00944 function loadGroups() {
00945 if ( is_null( $this->mGroups ) ) {
00946 $dbr = wfGetDB( DB_MASTER );
00947 $res = $dbr->select( 'user_groups',
00948 array( 'ug_group' ),
00949 array( 'ug_user' => $this->mId ),
00950 __METHOD__ );
00951 $this->mGroups = array();
00952 while( $row = $dbr->fetchObject( $res ) ) {
00953 $this->mGroups[] = $row->ug_group;
00954 }
00955 }
00956 }
00957
00964 function clearInstanceCache( $reloadFrom = false ) {
00965 $this->mNewtalk = -1;
00966 $this->mDatePreference = null;
00967 $this->mBlockedby = -1; # Unset
00968 $this->mHash = false;
00969 $this->mSkin = null;
00970 $this->mRights = null;
00971 $this->mEffectiveGroups = null;
00972
00973 if ( $reloadFrom ) {
00974 $this->mDataLoaded = false;
00975 $this->mFrom = $reloadFrom;
00976 }
00977 }
00978
00985 static function getDefaultOptions() {
00986 global $wgNamespacesToBeSearchedDefault;
00990 global $wgDefaultUserOptions, $wgContLang;
00991 $defOpt = $wgDefaultUserOptions + $wgContLang->getDefaultUserOptionOverrides();
00992
00996 $variant = $wgContLang->getPreferredVariant( false );
00997 $defOpt['variant'] = $variant;
00998 $defOpt['language'] = $variant;
00999
01000 foreach( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
01001 $defOpt['searchNs'.$nsnum] = $val;
01002 }
01003 return $defOpt;
01004 }
01005
01012 public static function getDefaultOption( $opt ) {
01013 $defOpts = self::getDefaultOptions();
01014 if( isset( $defOpts[$opt] ) ) {
01015 return $defOpts[$opt];
01016 } else {
01017 return '';
01018 }
01019 }
01020
01025 static function getToggles() {
01026 global $wgContLang, $wgUseRCPatrol;
01027 $extraToggles = array();
01028 wfRunHooks( 'UserToggles', array( &$extraToggles ) );
01029 if( $wgUseRCPatrol ) {
01030 $extraToggles[] = 'hidepatrolled';
01031 $extraToggles[] = 'newpageshidepatrolled';
01032 $extraToggles[] = 'watchlisthidepatrolled';
01033 }
01034 return array_merge( self::$mToggles, $extraToggles, $wgContLang->getExtraUserToggles() );
01035 }
01036
01037
01046 function getBlockedStatus( $bFromSlave = true ) {
01047 global $wgEnableSorbs, $wgProxyWhitelist;
01048
01049 if ( -1 != $this->mBlockedby ) {
01050 wfDebug( "User::getBlockedStatus: already loaded.\n" );
01051 return;
01052 }
01053
01054 wfProfileIn( __METHOD__ );
01055 wfDebug( __METHOD__.": checking...\n" );
01056
01057
01058
01059
01060
01061
01062 $this->load();
01063
01064 $this->mBlockedby = 0;
01065 $this->mHideName = 0;
01066 $this->mAllowUsertalk = 0;
01067 $ip = wfGetIP();
01068
01069 if ($this->isAllowed( 'ipblock-exempt' ) ) {
01070 # Exempt from all types of IP-block
01071 $ip = '';
01072 }
01073
01074 # User/IP blocking
01075 $this->mBlock = new Block();
01076 $this->mBlock->fromMaster( !$bFromSlave );
01077 if ( $this->mBlock->load( $ip , $this->mId ) ) {
01078 wfDebug( __METHOD__.": Found block.\n" );
01079 $this->mBlockedby = $this->mBlock->mBy;
01080 $this->mBlockreason = $this->mBlock->mReason;
01081 $this->mHideName = $this->mBlock->mHideName;
01082 $this->mAllowUsertalk = $this->mBlock->mAllowUsertalk;
01083 if ( $this->isLoggedIn() ) {
01084 $this->spreadBlock();
01085 }
01086 } else {
01087
01088
01089
01090 }
01091
01092 # Proxy blocking
01093 if ( !$this->isAllowed('proxyunbannable') && !in_array( $ip, $wgProxyWhitelist ) ) {
01094 # Local list
01095 if ( wfIsLocallyBlockedProxy( $ip ) ) {
01096 $this->mBlockedby = wfMsg( 'proxyblocker' );
01097 $this->mBlockreason = wfMsg( 'proxyblockreason' );
01098 }
01099
01100 # DNSBL
01101 if ( !$this->mBlockedby && $wgEnableSorbs && !$this->getID() ) {
01102 if ( $this->inSorbsBlacklist( $ip ) ) {
01103 $this->mBlockedby = wfMsg( 'sorbs' );
01104 $this->mBlockreason = wfMsg( 'sorbsreason' );
01105 }
01106 }
01107 }
01108
01109 # Extensions
01110 wfRunHooks( 'GetBlockedStatus', array( &$this ) );
01111
01112 wfProfileOut( __METHOD__ );
01113 }
01114
01121 function inSorbsBlacklist( $ip ) {
01122 global $wgEnableSorbs, $wgSorbsUrl;
01123
01124 return $wgEnableSorbs &&
01125 $this->inDnsBlacklist( $ip, $wgSorbsUrl );
01126 }
01127
01135 function inDnsBlacklist( $ip, $base ) {
01136 wfProfileIn( __METHOD__ );
01137
01138 $found = false;
01139 $host = '';
01140
01141 if( IP::isIPv4($ip) ) {
01142 # Make hostname
01143 $host = "$ip.$base";
01144
01145 # Send query
01146 $ipList = gethostbynamel( $host );
01147
01148 if( $ipList ) {
01149 wfDebug( "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
01150 $found = true;
01151 } else {
01152 wfDebug( "Requested $host, not found in $base.\n" );
01153 }
01154 }
01155
01156 wfProfileOut( __METHOD__ );
01157 return $found;
01158 }
01159
01165 public function isPingLimitable() {
01166 global $wgRateLimitsExcludedGroups;
01167 global $wgRateLimitsExcludedIPs;
01168 if( array_intersect( $this->getEffectiveGroups(), $wgRateLimitsExcludedGroups ) ) {
01169
01170 return false;
01171 }
01172 if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) {
01173
01174
01175
01176 return false;
01177 }
01178 return !$this->isAllowed('noratelimit');
01179 }
01180
01191 function pingLimiter( $action='edit' ) {
01192
01193 # Call the 'PingLimiter' hook
01194 $result = false;
01195 if( !wfRunHooks( 'PingLimiter', array( &$this, $action, $result ) ) ) {
01196 return $result;
01197 }
01198
01199 global $wgRateLimits;
01200 if( !isset( $wgRateLimits[$action] ) ) {
01201 return false;
01202 }
01203
01204 # Some groups shouldn't trigger the ping limiter, ever
01205 if( !$this->isPingLimitable() )
01206 return false;
01207
01208 global $wgMemc, $wgRateLimitLog;
01209 wfProfileIn( __METHOD__ );
01210
01211 $limits = $wgRateLimits[$action];
01212 $keys = array();
01213 $id = $this->getId();
01214 $ip = wfGetIP();
01215 $userLimit = false;
01216
01217 if( isset( $limits['anon'] ) && $id == 0 ) {
01218 $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
01219 }
01220
01221 if( isset( $limits['user'] ) && $id != 0 ) {
01222 $userLimit = $limits['user'];
01223 }
01224 if( $this->isNewbie() ) {
01225 if( isset( $limits['newbie'] ) && $id != 0 ) {
01226 $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
01227 }
01228 if( isset( $limits['ip'] ) ) {
01229 $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
01230 }
01231 $matches = array();
01232 if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
01233 $subnet = $matches[1];
01234 $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
01235 }
01236 }
01237
01238
01239 foreach ( $this->getGroups() as $group ) {
01240 if ( isset( $limits[$group] ) ) {
01241 if ( $userLimit === false || $limits[$group] > $userLimit ) {
01242 $userLimit = $limits[$group];
01243 }
01244 }
01245 }
01246
01247 if ( $userLimit !== false ) {
01248 wfDebug( __METHOD__.": effective user limit: $userLimit\n" );
01249 $keys[ wfMemcKey( 'limiter', $action, 'user', $id ) ] = $userLimit;
01250 }
01251
01252 $triggered = false;
01253 foreach( $keys as $key => $limit ) {
01254 list( $max, $period ) = $limit;
01255 $summary = "(limit $max in {$period}s)";
01256 $count = $wgMemc->get( $key );
01257 if( $count ) {
01258 if( $count > $max ) {
01259 wfDebug( __METHOD__.": tripped! $key at $count $summary\n" );
01260 if( $wgRateLimitLog ) {
01261 @error_log( wfTimestamp( TS_MW ) . ' ' . wfWikiID() . ': ' . $this->getName() . " tripped $key at $count $summary\n", 3, $wgRateLimitLog );
01262 }
01263 $triggered = true;
01264 } else {
01265 wfDebug( __METHOD__.": ok. $key at $count $summary\n" );
01266 }
01267 } else {
01268 wfDebug( __METHOD__.": adding record for $key $summary\n" );
01269 $wgMemc->add( $key, 1, intval( $period ) );
01270 }
01271 $wgMemc->incr( $key );
01272 }
01273
01274 wfProfileOut( __METHOD__ );
01275 return $triggered;
01276 }
01277
01284 function isBlocked( $bFromSlave = true ) {
01285 wfDebug( "User::isBlocked: enter\n" );
01286 $this->getBlockedStatus( $bFromSlave );
01287 return $this->mBlockedby !== 0;
01288 }
01289
01297 function isBlockedFrom( $title, $bFromSlave = false ) {
01298 global $wgBlockAllowsUTEdit;
01299 wfProfileIn( __METHOD__ );
01300 wfDebug( __METHOD__.": enter\n" );
01301
01302 wfDebug( __METHOD__.": asking isBlocked()\n" );
01303 $blocked = $this->isBlocked( $bFromSlave );
01304 $allowUsertalk = ($wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false);
01305 # If a user's name is suppressed, they cannot make edits anywhere
01306 if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName() &&
01307 $title->getNamespace() == NS_USER_TALK ) {
01308 $blocked = false;
01309 wfDebug( __METHOD__.": self-talk page, ignoring any blocks\n" );
01310 }
01311 wfProfileOut( __METHOD__ );
01312 return $blocked;
01313 }
01314
01319 function blockedBy() {
01320 $this->getBlockedStatus();
01321 return $this->mBlockedby;
01322 }
01323
01328 function blockedFor() {
01329 $this->getBlockedStatus();
01330 return $this->mBlockreason;
01331 }
01332
01337 function getBlockId() {
01338 $this->getBlockedStatus();
01339 return ($this->mBlock ? $this->mBlock->mId : false);
01340 }
01341
01350 function isBlockedGlobally( $ip = '' ) {
01351 if( $this->mBlockedGlobally !== null ) {
01352 return $this->mBlockedGlobally;
01353 }
01354
01355 if( IP::isIPAddress( $this->getName() ) ) {
01356 $ip = $this->getName();
01357 } else if( !$ip ) {
01358 $ip = wfGetIP();
01359 }
01360 $blocked = false;
01361 wfRunHooks( 'UserIsBlockedGlobally', array( &$this, $ip, &$blocked ) );
01362 $this->mBlockedGlobally = (bool)$blocked;
01363 return $this->mBlockedGlobally;
01364 }
01365
01371 function isLocked() {
01372 if( $this->mLocked !== null ) {
01373 return $this->mLocked;
01374 }
01375 global $wgAuth;
01376 $authUser = $wgAuth->getUserInstance( $this );
01377 $this->mLocked = (bool)$authUser->isLocked();
01378 return $this->mLocked;
01379 }
01380
01386 function isHidden() {
01387 if( $this->mHideName !== null ) {
01388 return $this->mHideName;
01389 }
01390 $this->getBlockedStatus();
01391 if( !$this->mHideName ) {
01392 global $wgAuth;
01393 $authUser = $wgAuth->getUserInstance( $this );
01394 $this->mHideName = (bool)$authUser->isHidden();
01395 }
01396 return $this->mHideName;
01397 }
01398
01403 function getId() {
01404 if( $this->mId === null and $this->mName !== null
01405 and User::isIP( $this->mName ) ) {
01406
01407 return 0;
01408 } elseif( $this->mId === null ) {
01409
01410 $this->load();
01411 }
01412 return $this->mId;
01413 }
01414
01419 function setId( $v ) {
01420 $this->mId = $v;
01421 $this->clearInstanceCache( 'id' );
01422 }
01423
01428 function getName() {
01429 if ( !$this->mDataLoaded && $this->mFrom == 'name' ) {
01430 # Special case optimisation
01431 return $this->mName;
01432 } else {
01433 $this->load();
01434 if ( $this->mName === false ) {
01435 # Clean up IPs
01436 $this->mName = IP::sanitizeIP( wfGetIP() );
01437 }
01438 return $this->mName;
01439 }
01440 }
01441
01455 function setName( $str ) {
01456 $this->load();
01457 $this->mName = $str;
01458 }
01459
01464 function getTitleKey() {
01465 return str_replace( ' ', '_', $this->getName() );
01466 }
01467
01472 function getNewtalk() {
01473 $this->load();
01474
01475 # Load the newtalk status if it is unloaded (mNewtalk=-1)
01476 if( $this->mNewtalk === -1 ) {
01477 $this->mNewtalk = false; # reset talk page status
01478
01479 # Check memcached separately for anons, who have no
01480 # entire User object stored in there.
01481 if( !$this->mId ) {
01482 global $wgMemc;
01483 $key = wfMemcKey( 'newtalk', 'ip', $this->getName() );
01484 $newtalk = $wgMemc->get( $key );
01485 if( strval( $newtalk ) !== '' ) {
01486 $this->mNewtalk = (bool)$newtalk;
01487 } else {
01488
01489
01490 $this->mNewtalk = $this->checkNewtalk( 'user_ip', $this->getName(), true );
01491 $wgMemc->set( $key, (int)$this->mNewtalk, 1800 );
01492 }
01493 } else {
01494 $this->mNewtalk = $this->checkNewtalk( 'user_id', $this->mId );
01495 }
01496 }
01497
01498 return (bool)$this->mNewtalk;
01499 }
01500
01505 function getNewMessageLinks() {
01506 $talks = array();
01507 if (!wfRunHooks('UserRetrieveNewTalks', array(&$this, &$talks)))
01508 return $talks;
01509
01510 if (!$this->getNewtalk())
01511 return array();
01512 $up = $this->getUserPage();
01513 $utp = $up->getTalkPage();
01514 return array(array("wiki" => wfWikiID(), "link" => $utp->getLocalURL()));
01515 }
01516
01517
01528 function checkNewtalk( $field, $id, $fromMaster = false ) {
01529 if ( $fromMaster ) {
01530 $db = wfGetDB( DB_MASTER );
01531 } else {
01532 $db = wfGetDB( DB_SLAVE );
01533 }
01534 $ok = $db->selectField( 'user_newtalk', $field,
01535 array( $field => $id ), __METHOD__ );
01536 return $ok !== false;
01537 }
01538
01546 function updateNewtalk( $field, $id ) {
01547 $dbw = wfGetDB( DB_MASTER );
01548 $dbw->insert( 'user_newtalk',
01549 array( $field => $id ),
01550 __METHOD__,
01551 'IGNORE' );
01552 if ( $dbw->affectedRows() ) {
01553 wfDebug( __METHOD__.": set on ($field, $id)\n" );
01554 return true;
01555 } else {
01556 wfDebug( __METHOD__." already set ($field, $id)\n" );
01557 return false;
01558 }
01559 }
01560
01568 function deleteNewtalk( $field, $id ) {
01569 $dbw = wfGetDB( DB_MASTER );
01570 $dbw->delete( 'user_newtalk',
01571 array( $field => $id ),
01572 __METHOD__ );
01573 if ( $dbw->affectedRows() ) {
01574 wfDebug( __METHOD__.": killed on ($field, $id)\n" );
01575 return true;
01576 } else {
01577 wfDebug( __METHOD__.": already gone ($field, $id)\n" );
01578 return false;
01579 }
01580 }
01581
01586 function setNewtalk( $val ) {
01587 if( wfReadOnly() ) {
01588 return;
01589 }
01590
01591 $this->load();
01592 $this->mNewtalk = $val;
01593
01594 if( $this->isAnon() ) {
01595 $field = 'user_ip';
01596 $id = $this->getName();
01597 } else {
01598 $field = 'user_id';
01599 $id = $this->getId();
01600 }
01601 global $wgMemc;
01602
01603 if( $val ) {
01604 $changed = $this->updateNewtalk( $field, $id );
01605 } else {
01606 $changed = $this->deleteNewtalk( $field, $id );
01607 }
01608
01609 if( $this->isAnon() ) {
01610
01611
01612 $key = wfMemcKey( 'newtalk', 'ip', $id );
01613 $wgMemc->set( $key, $val ? 1 : 0, 1800 );
01614 }
01615 if ( $changed ) {
01616 $this->invalidateCache();
01617 }
01618 }
01619
01625 private static function newTouchedTimestamp() {
01626 global $wgClockSkewFudge;
01627 return wfTimestamp( TS_MW, time() + $wgClockSkewFudge );
01628 }
01629
01637 private function clearSharedCache() {
01638 $this->load();
01639 if( $this->mId ) {
01640 global $wgMemc;
01641 $wgMemc->delete( wfMemcKey( 'user', 'id', $this->mId ) );
01642 }
01643 }
01644
01650 function invalidateCache() {
01651 $this->load();
01652 if( $this->mId ) {
01653 $this->mTouched = self::newTouchedTimestamp();
01654
01655 $dbw = wfGetDB( DB_MASTER );
01656 $dbw->update( 'user',
01657 array( 'user_touched' => $dbw->timestamp( $this->mTouched ) ),
01658 array( 'user_id' => $this->mId ),
01659 __METHOD__ );
01660
01661 $this->clearSharedCache();
01662 }
01663 }
01664
01669 function validateCache( $timestamp ) {
01670 $this->load();
01671 return ($timestamp >= $this->mTouched);
01672 }
01673
01677 function getTouched() {
01678 $this->load();
01679 return $this->mTouched;
01680 }
01681
01696 function setPassword( $str ) {
01697 global $wgAuth;
01698
01699 if( $str !== null ) {
01700 if( !$wgAuth->allowPasswordChange() ) {
01701 throw new PasswordError( wfMsg( 'password-change-forbidden' ) );
01702 }
01703
01704 if( !$this->isValidPassword( $str ) ) {
01705 global $wgMinimalPasswordLength;
01706 throw new PasswordError( wfMsgExt( 'passwordtooshort', array( 'parsemag' ),
01707 $wgMinimalPasswordLength ) );
01708 }
01709 }
01710
01711 if( !$wgAuth->setPassword( $this, $str ) ) {
01712 throw new PasswordError( wfMsg( 'externaldberror' ) );
01713 }
01714
01715 $this->setInternalPassword( $str );
01716
01717 return true;
01718 }
01719
01725 function setInternalPassword( $str ) {
01726 $this->load();
01727 $this->setToken();
01728
01729 if( $str === null ) {
01730
01731 $this->mPassword = '';
01732 } else {
01733 $this->mPassword = self::crypt( $str );
01734 }
01735 $this->mNewpassword = '';
01736 $this->mNewpassTime = null;
01737 }
01738
01743 function getToken() {
01744 $this->load();
01745 return $this->mToken;
01746 }
01747
01755 function setToken( $token = false ) {
01756 global $wgSecretKey, $wgProxyKey;
01757 $this->load();
01758 if ( !$token ) {
01759 if ( $wgSecretKey ) {
01760 $key = $wgSecretKey;
01761 } elseif ( $wgProxyKey ) {
01762 $key = $wgProxyKey;
01763 } else {
01764 $key = microtime();
01765 }
01766 $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId );
01767 } else {
01768 $this->mToken = $token;
01769 }
01770 }
01771
01778 function setCookiePassword( $str ) {
01779 $this->load();
01780 $this->mCookiePassword = md5( $str );
01781 }
01782
01789 function setNewpassword( $str, $throttle = true ) {
01790 $this->load();
01791 $this->mNewpassword = self::crypt( $str );
01792 if ( $throttle ) {
01793 $this->mNewpassTime = wfTimestampNow();
01794 }
01795 }
01796
01802 function isPasswordReminderThrottled() {
01803 global $wgPasswordReminderResendTime;
01804 $this->load();
01805 if ( !$this->mNewpassTime || !$wgPasswordReminderResendTime ) {
01806 return false;
01807 }
01808 $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgPasswordReminderResendTime * 3600;
01809 return time() < $expiry;
01810 }
01811
01816 function getEmail() {
01817 $this->load();
01818 wfRunHooks( 'UserGetEmail', array( $this, &$this->mEmail ) );
01819 return $this->mEmail;
01820 }
01821
01826 function getEmailAuthenticationTimestamp() {
01827 $this->load();
01828 wfRunHooks( 'UserGetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
01829 return $this->mEmailAuthenticated;
01830 }
01831
01836 function setEmail( $str ) {
01837 $this->load();
01838 $this->mEmail = $str;
01839 wfRunHooks( 'UserSetEmail', array( $this, &$this->mEmail ) );
01840 }
01841
01846 function getRealName() {
01847 $this->load();
01848 return $this->mRealName;
01849 }
01850
01855 function setRealName( $str ) {
01856 $this->load();
01857 $this->mRealName = $str;
01858 }
01859
01869 function getOption( $oname, $defaultOverride = '' ) {
01870 $this->load();
01871
01872 if ( is_null( $this->mOptions ) ) {
01873 if($defaultOverride != '') {
01874 return $defaultOverride;
01875 }
01876 $this->mOptions = User::getDefaultOptions();
01877 }
01878
01879 if ( array_key_exists( $oname, $this->mOptions ) ) {
01880 return trim( $this->mOptions[$oname] );
01881 } else {
01882 return $defaultOverride;
01883 }
01884 }
01885
01893 function getBoolOption( $oname ) {
01894 return (bool)$this->getOption( $oname );
01895 }
01896
01897
01906 function getIntOption( $oname, $defaultOverride=0 ) {
01907 $val = $this->getOption( $oname );
01908 if( $val == '' ) {
01909 $val = $defaultOverride;
01910 }
01911 return intval( $val );
01912 }
01913
01920 function setOption( $oname, $val ) {
01921 $this->load();
01922 if ( is_null( $this->mOptions ) ) {
01923 $this->mOptions = User::getDefaultOptions();
01924 }
01925 if ( $oname == 'skin' ) {
01926 # Clear cached skin, so the new one displays immediately in Special:Preferences
01927 unset( $this->mSkin );
01928 }
01929
01930
01931 if( $val ) {
01932 $val = str_replace( "\r\n", "\n", $val );
01933 $val = str_replace( "\r", "\n", $val );
01934 $val = str_replace( "\n", " ", $val );
01935 }
01936
01937 global $wgDefaultUserOptions;
01938 if( is_null($val) && isset($wgDefaultUserOptions[$oname]) ) {
01939 $val = $wgDefaultUserOptions[$oname];
01940 }
01941 $this->mOptions[$oname] = $val;
01942 }
01943
01947 function restoreOptions() {
01948 $this->mOptions = User::getDefaultOptions();
01949 }
01950
01955 function getDatePreference() {
01956
01957 if ( is_null( $this->mDatePreference ) ) {
01958 global $wgLang;
01959 $value = $this->getOption( 'date' );
01960 $map = $wgLang->getDatePreferenceMigrationMap();
01961 if ( isset( $map[$value] ) ) {
01962 $value = $map[$value];
01963 }
01964 $this->mDatePreference = $value;
01965 }
01966 return $this->mDatePreference;
01967 }
01968
01973 function getRights() {
01974 if ( is_null( $this->mRights ) ) {
01975 $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
01976 wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
01977
01978 $this->mRights = array_values( $this->mRights );
01979 }
01980 return $this->mRights;
01981 }
01982
01988 function getGroups() {
01989 $this->load();
01990 return $this->mGroups;
01991 }
01992
02000 function getEffectiveGroups( $recache = false ) {
02001 if ( $recache || is_null( $this->mEffectiveGroups ) ) {
02002 $this->mEffectiveGroups = $this->getGroups();
02003 $this->mEffectiveGroups[] = '*';
02004 if( $this->getId() ) {
02005 $this->mEffectiveGroups[] = 'user';
02006
02007 $this->mEffectiveGroups = array_unique( array_merge(
02008 $this->mEffectiveGroups,
02009 Autopromote::getAutopromoteGroups( $this )
02010 ) );
02011
02012 # Hook for additional groups
02013 wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
02014 }
02015 }
02016 return $this->mEffectiveGroups;
02017 }
02018
02023 function getEditCount() {
02024 if ($this->getId()) {
02025 if ( !isset( $this->mEditCount ) ) {
02026
02027 $this->mEditCount = User::edits($this->mId);
02028 }
02029 return $this->mEditCount;
02030 } else {
02031
02032 return null;
02033 }
02034 }
02035
02041 function addGroup( $group ) {
02042 $dbw = wfGetDB( DB_MASTER );
02043 if( $this->getId() ) {
02044 $dbw->insert( 'user_groups',
02045 array(
02046 'ug_user' => $this->getID(),
02047 'ug_group' => $group,
02048 ),
02049 'User::addGroup',
02050 array( 'IGNORE' ) );
02051 }
02052
02053 $this->loadGroups();
02054 $this->mGroups[] = $group;
02055 $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
02056
02057 $this->invalidateCache();
02058 }
02059
02065 function removeGroup( $group ) {
02066 $this->load();
02067 $dbw = wfGetDB( DB_MASTER );
02068 $dbw->delete( 'user_groups',
02069 array(
02070 'ug_user' => $this->getID(),
02071 'ug_group' => $group,
02072 ),
02073 'User::removeGroup' );
02074
02075 $this->loadGroups();
02076 $this->mGroups = array_diff( $this->mGroups, array( $group ) );
02077 $this->mRights = User::getGroupPermissions( $this->getEffectiveGroups( true ) );
02078
02079 $this->invalidateCache();
02080 }
02081
02082
02087 function isLoggedIn() {
02088 return $this->getID() != 0;
02089 }
02090
02095 function isAnon() {
02096 return !$this->isLoggedIn();
02097 }
02098
02104 function isBot() {
02105 wfDeprecated( __METHOD__ );
02106 return $this->isAllowed( 'bot' );
02107 }
02108
02114 function isAllowed( $action = '' ) {
02115 if ( $action === '' )
02116 return true;
02117 # Patrolling may not be enabled
02118 if( $action === 'patrol' || $action === 'autopatrol' ) {
02119 global $wgUseRCPatrol, $wgUseNPPatrol;
02120 if( !$wgUseRCPatrol && !$wgUseNPPatrol )
02121 return false;
02122 }
02123 # Use strict parameter to avoid matching numeric 0 accidentally inserted
02124 # by misconfiguration: 0 == 'foo'
02125 return in_array( $action, $this->getRights(), true );
02126 }
02127
02132 public function useRCPatrol() {
02133 global $wgUseRCPatrol;
02134 return( $wgUseRCPatrol && ($this->isAllowed('patrol') || $this->isAllowed('patrolmarks')) );
02135 }
02136
02141 public function useNPPatrol() {
02142 global $wgUseRCPatrol, $wgUseNPPatrol;
02143 return( ($wgUseRCPatrol || $wgUseNPPatrol) && ($this->isAllowed('patrol') || $this->isAllowed('patrolmarks')) );
02144 }
02145
02151 function &getSkin() {
02152 global $wgRequest, $wgAllowUserSkin, $wgDefaultSkin;
02153 if ( ! isset( $this->mSkin ) ) {
02154 wfProfileIn( __METHOD__ );
02155
02156 if( $wgAllowUserSkin ) {
02157 # get the user skin
02158 $userSkin = $this->getOption( 'skin' );
02159 $userSkin = $wgRequest->getVal('useskin', $userSkin);
02160 } else {
02161 # if we're not allowing users to override, then use the default
02162 $userSkin = $wgDefaultSkin;
02163 }
02164
02165 $this->mSkin =& Skin::newFromKey( $userSkin );
02166 wfProfileOut( __METHOD__ );
02167 }
02168 return $this->mSkin;
02169 }
02170
02176 function isWatched( $title ) {
02177 $wl = WatchedItem::fromUserTitle( $this, $title );
02178 return $wl->isWatched();
02179 }
02180
02185 function addWatch( $title ) {
02186 $wl = WatchedItem::fromUserTitle( $this, $title );
02187 $wl->addWatch();
02188 $this->invalidateCache();
02189 }
02190
02195 function removeWatch( $title ) {
02196 $wl = WatchedItem::fromUserTitle( $this, $title );
02197 $wl->removeWatch();
02198 $this->invalidateCache();
02199 }
02200
02207 function clearNotification( &$title ) {
02208 global $wgUser, $wgUseEnotif, $wgShowUpdatedMarker;
02209
02210 # Do nothing if the database is locked to writes
02211 if( wfReadOnly() ) {
02212 return;
02213 }
02214
02215 if ($title->getNamespace() == NS_USER_TALK &&
02216 $title->getText() == $this->getName() ) {
02217 if (!wfRunHooks('UserClearNewTalkNotification', array(&$this)))
02218 return;
02219 $this->setNewtalk( false );
02220 }
02221
02222 if( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
02223 return;
02224 }
02225
02226 if( $this->isAnon() ) {
02227
02228 return;
02229 }
02230
02231
02232
02233
02234
02235 if ($title->getNamespace() == NS_USER_TALK &&
02236 $title->getText() == $wgUser->getName())
02237 {
02238 $watched = true;
02239 } elseif ( $this->getId() == $wgUser->getId() ) {
02240 $watched = $title->userIsWatching();
02241 } else {
02242 $watched = true;
02243 }
02244
02245
02246
02247 if ( $watched ) {
02248 $dbw = wfGetDB( DB_MASTER );
02249 $dbw->update( 'watchlist',
02250 array(
02251 'wl_notificationtimestamp' => NULL
02252 ), array(
02253 'wl_title' => $title->getDBkey(),
02254 'wl_namespace' => $title->getNamespace(),
02255 'wl_user' => $this->getID()
02256 ), __METHOD__
02257 );
02258 }
02259 }
02260
02268 function clearAllNotifications( $currentUser ) {
02269 global $wgUseEnotif, $wgShowUpdatedMarker;
02270 if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
02271 $this->setNewtalk( false );
02272 return;
02273 }
02274 if( $currentUser != 0 ) {
02275 $dbw = wfGetDB( DB_MASTER );
02276 $dbw->update( 'watchlist',
02277 array(
02278 'wl_notificationtimestamp' => NULL
02279 ), array(
02280 'wl_user' => $currentUser
02281 ), __METHOD__
02282 );
02283 # We also need to clear here the "you have new message" notification for the own user_talk page
02284 # This is cleared one page view later in Article::viewUpdates();
02285 }
02286 }
02287
02293 function encodeOptions() {
02294 $this->load();
02295 if ( is_null( $this->mOptions ) ) {
02296 $this->mOptions = User::getDefaultOptions();
02297 }
02298 $a = array();
02299 foreach ( $this->mOptions as $oname => $oval ) {
02300 array_push( $a, $oname.'='.$oval );
02301 }
02302 $s = implode( "\n", $a );
02303 return $s;
02304 }
02305
02311 function decodeOptions( $str ) {
02312 $this->mOptions = array();
02313 $a = explode( "\n", $str );
02314 foreach ( $a as $s ) {
02315 $m = array();
02316 if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
02317 $this->mOptions[$m[1]] = $m[2];
02318 }
02319 }
02320 }
02321
02330 protected function setCookie( $name, $value, $exp=0 ) {
02331 global $wgRequest;
02332 $wgRequest->response()->setcookie( $name, $value, $exp );
02333 }
02334
02339 protected function clearCookie( $name ) {
02340 $this->setCookie( $name, '', time() - 86400 );
02341 }
02342
02346 function setCookies() {
02347 $this->load();
02348 if ( 0 == $this->mId ) return;
02349 $session = array(
02350 'wsUserID' => $this->mId,
02351 'wsToken' => $this->mToken,
02352 'wsUserName' => $this->getName()
02353 );
02354 $cookies = array(
02355 'UserID' => $this->mId,
02356 'UserName' => $this->getName(),
02357 );
02358 if ( 1 == $this->getOption( 'rememberpassword' ) ) {
02359 $cookies['Token'] = $this->mToken;
02360 } else {
02361 $cookies['Token'] = false;
02362 }
02363
02364 wfRunHooks( 'UserSetCookies', array( $this, &$session, &$cookies ) );
02365 #check for null, since the hook could cause a null value
02366 if ( !is_null( $session ) && isset( $_SESSION ) ){
02367 $_SESSION = $session + $_SESSION;
02368 }
02369 foreach ( $cookies as $name => $value ) {
02370 if ( $value === false ) {
02371 $this->clearCookie( $name );
02372 } else {
02373 $this->setCookie( $name, $value );
02374 }
02375 }
02376 }
02377
02381 function logout() {
02382 global $wgUser;
02383 if( wfRunHooks( 'UserLogout', array(&$this) ) ) {
02384 $this->doLogout();
02385 }
02386 }
02387
02393 function doLogout() {
02394 $this->clearInstanceCache( 'defaults' );
02395
02396 $_SESSION['wsUserID'] = 0;
02397
02398 $this->clearCookie( 'UserID' );
02399 $this->clearCookie( 'Token' );
02400
02401 # Remember when user logged out, to prevent seeing cached pages
02402 $this->setCookie( 'LoggedOut', wfTimestampNow(), time() + 86400 );
02403 }
02404
02409 function saveSettings() {
02410 $this->load();
02411 if ( wfReadOnly() ) { return; }
02412 if ( 0 == $this->mId ) { return; }
02413
02414 $this->mTouched = self::newTouchedTimestamp();
02415
02416 $dbw = wfGetDB( DB_MASTER );
02417 $dbw->update( 'user',
02418 array(
02419 'user_name' => $this->mName,
02420 'user_password' => $this->mPassword,
02421 'user_newpassword' => $this->mNewpassword,
02422 'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
02423 'user_real_name' => $this->mRealName,
02424 'user_email' => $this->mEmail,
02425 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
02426 'user_options' => $this->encodeOptions(),
02427 'user_touched' => $dbw->timestamp($this->mTouched),
02428 'user_token' => $this->mToken,
02429 'user_email_token' => $this->mEmailToken,
02430 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
02431 ), array(
02432 'user_id' => $this->mId
02433 ), __METHOD__
02434 );
02435 wfRunHooks( 'UserSaveSettings', array( $this ) );
02436 $this->clearSharedCache();
02437 $this->getUserPage()->invalidateCache();
02438 }
02439
02443 function idForName() {
02444 $s = trim( $this->getName() );
02445 if ( $s === '' ) return 0;
02446
02447 $dbr = wfGetDB( DB_SLAVE );
02448 $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
02449 if ( $id === false ) {
02450 $id = 0;
02451 }
02452 return $id;
02453 }
02454
02471 static function createNew( $name, $params = array() ) {
02472 $user = new User;
02473 $user->load();
02474 if ( isset( $params['options'] ) ) {
02475 $user->mOptions = $params['options'] + $user->mOptions;
02476 unset( $params['options'] );
02477 }
02478 $dbw = wfGetDB( DB_MASTER );
02479 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
02480 $fields = array(
02481 'user_id' => $seqVal,
02482 'user_name' => $name,
02483 'user_password' => $user->mPassword,
02484 'user_newpassword' => $user->mNewpassword,
02485 'user_newpass_time' => $dbw->timestamp( $user->mNewpassTime ),
02486 'user_email' => $user->mEmail,
02487 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
02488 'user_real_name' => $user->mRealName,
02489 'user_options' => $user->encodeOptions(),
02490 'user_token' => $user->mToken,
02491 'user_registration' => $dbw->timestamp( $user->mRegistration ),
02492 'user_editcount' => 0,
02493 );
02494 foreach ( $params as $name => $value ) {
02495 $fields["user_$name"] = $value;
02496 }
02497 $dbw->insert( 'user', $fields, __METHOD__, array( 'IGNORE' ) );
02498 if ( $dbw->affectedRows() ) {
02499 $newUser = User::newFromId( $dbw->insertId() );
02500 } else {
02501 $newUser = null;
02502 }
02503 return $newUser;
02504 }
02505
02509 function addToDatabase() {
02510 $this->load();
02511 $dbw = wfGetDB( DB_MASTER );
02512 $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
02513 $dbw->insert( 'user',
02514 array(
02515 'user_id' => $seqVal,
02516 'user_name' => $this->mName,
02517 'user_password' => $this->mPassword,
02518 'user_newpassword' => $this->mNewpassword,
02519 'user_newpass_time' => $dbw->timestamp( $this->mNewpassTime ),
02520 'user_email' => $this->mEmail,
02521 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
02522 'user_real_name' => $this->mRealName,
02523 'user_options' => $this->encodeOptions(),
02524 'user_token' => $this->mToken,
02525 'user_registration' => $dbw->timestamp( $this->mRegistration ),
02526 'user_editcount' => 0,
02527 ), __METHOD__
02528 );
02529 $this->mId = $dbw->insertId();
02530
02531
02532 $this->clearInstanceCache();
02533 }
02534
02539 function spreadBlock() {
02540 wfDebug( __METHOD__."()\n" );
02541 $this->load();
02542 if ( $this->mId == 0 ) {
02543 return;
02544 }
02545
02546 $userblock = Block::newFromDB( '', $this->mId );
02547 if ( !$userblock ) {
02548 return;
02549 }
02550
02551 $userblock->doAutoblock( wfGetIp() );
02552
02553 }
02554
02568 function getPageRenderingHash() {
02569 global $wgUseDynamicDates, $wgRenderHashAppend, $wgLang, $wgContLang;
02570 if( $this->mHash ){
02571 return $this->mHash;
02572 }
02573
02574
02575
02576
02577 $confstr = $this->getOption( 'math' );
02578 $confstr .= '!' . $this->getOption( 'stubthreshold' );
02579 if ( $wgUseDynamicDates ) {
02580 $confstr .= '!' . $this->getDatePreference();
02581 }
02582 $confstr .= '!' . ($this->getOption( 'numberheadings' ) ? '1' : '');
02583 $confstr .= '!' . $wgLang->getCode();
02584 $confstr .= '!' . $this->getOption( 'thumbsize' );
02585
02586 $extra = $wgContLang->getExtraHashOptions();
02587 $confstr .= $extra;
02588
02589 $confstr .= $wgRenderHashAppend;
02590
02591
02592
02593 wfRunHooks( 'PageRenderingHash', array( &$confstr ) );
02594
02595
02596 $confstr = str_replace( ' ', '_', $confstr );
02597 $this->mHash = $confstr;
02598 return $confstr;
02599 }
02600
02605 function isBlockedFromCreateAccount() {
02606 $this->getBlockedStatus();
02607 return $this->mBlock && $this->mBlock->mCreateAccount;
02608 }
02609
02614 function isBlockedFromEmailuser() {
02615 $this->getBlockedStatus();
02616 return $this->mBlock && $this->mBlock->mBlockEmail;
02617 }
02618
02623 function isAllowedToCreateAccount() {
02624 return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
02625 }
02626
02630 function setLoaded( $loaded ) {
02631 wfDeprecated( __METHOD__ );
02632 }
02633
02639 function getUserPage() {
02640 return Title::makeTitle( NS_USER, $this->getName() );
02641 }
02642
02648 function getTalkPage() {
02649 $title = $this->getUserPage();
02650 return $title->getTalkPage();
02651 }
02652
02658 function getMaxID() {
02659 static $res;
02660
02661 if ( isset( $res ) )
02662 return $res;
02663 else {
02664 $dbr = wfGetDB( DB_SLAVE );
02665 return $res = $dbr->selectField( 'user', 'max(user_id)', false, 'User::getMaxID' );
02666 }
02667 }
02668
02674 function isNewbie() {
02675 return !$this->isAllowed( 'autoconfirmed' );
02676 }
02677
02684 public function isActiveEditor() {
02685 global $wgActiveUserEditCount, $wgActiveUserDays;
02686 $dbr = wfGetDB( DB_SLAVE );
02687
02688
02689 $cutoff_unixtime = time() - ( $wgActiveUserDays * 86400 );
02690 $cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
02691 $oldTime = $dbr->addQuotes( $dbr->timestamp( $cutoff_unixtime ) );
02692
02693 $res = $dbr->select( 'revision', '1',
02694 array( 'rev_user_text' => $this->getName(), "rev_timestamp > $oldTime"),
02695 __METHOD__,
02696 array('LIMIT' => $wgActiveUserEditCount ) );
02697
02698 $count = $dbr->numRows($res);
02699 $dbr->freeResult($res);
02700
02701 return $count == $wgActiveUserEditCount;
02702 }
02703
02709 function checkPassword( $password ) {
02710 global $wgAuth;
02711 $this->load();
02712
02713
02714
02715
02716
02717
02718 if( !$this->isValidPassword( $password ) ) {
02719 return false;
02720 }
02721
02722 if( $wgAuth->authenticate( $this->getName(), $password ) ) {
02723 return true;
02724 } elseif( $wgAuth->strict() ) {
02725
02726 return false;
02727 } elseif( $wgAuth->strictUserAuth( $this->getName() ) ) {
02728
02729 return false;
02730 }
02731 if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
02732 return true;
02733 } elseif ( function_exists( 'iconv' ) ) {
02734 # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
02735 # Check for this with iconv
02736 $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
02737 if ( self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) ) {
02738 return true;
02739 }
02740 }
02741 return false;
02742 }
02743
02749 function checkTemporaryPassword( $plaintext ) {
02750 global $wgNewPasswordExpiry;
02751 if( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) {
02752 $this->load();
02753 $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgNewPasswordExpiry;
02754 return ( time() < $expiry );
02755 } else {
02756 return false;
02757 }
02758 }
02759
02769 function editToken( $salt = '' ) {
02770 if ( $this->isAnon() ) {
02771 return EDIT_TOKEN_SUFFIX;
02772 } else {
02773 if( !isset( $_SESSION['wsEditToken'] ) ) {
02774 $token = $this->generateToken();
02775 $_SESSION['wsEditToken'] = $token;
02776 } else {
02777 $token = $_SESSION['wsEditToken'];
02778 }
02779 if( is_array( $salt ) ) {
02780 $salt = implode( '|', $salt );
02781 }
02782 return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX;
02783 }
02784 }
02785
02792 function generateToken( $salt = '' ) {
02793 $token = dechex( mt_rand() ) . dechex( mt_rand() );
02794 return md5( $token . $salt );
02795 }
02796
02807 function matchEditToken( $val, $salt = '' ) {
02808 $sessionToken = $this->editToken( $salt );
02809 if ( $val != $sessionToken ) {
02810 wfDebug( "User::matchEditToken: broken session data\n" );
02811 }
02812 return $val == $sessionToken;
02813 }
02814
02823 function matchEditTokenNoSuffix( $val, $salt = '' ) {
02824 $sessionToken = $this->editToken( $salt );
02825 return substr( $sessionToken, 0, 32 ) == substr( $val, 0, 32 );
02826 }
02827
02834 function sendConfirmationMail() {
02835 global $wgLang;
02836 $expiration = null;
02837 $token = $this->confirmationToken( $expiration );
02838 $url = $this->confirmationTokenUrl( $token );
02839 $invalidateURL = $this->invalidationTokenUrl( $token );
02840 $this->saveSettings();
02841
02842 return $this->sendMail( wfMsg( 'confirmemail_subject' ),
02843 wfMsg( 'confirmemail_body',
02844 wfGetIP(),
02845 $this->getName(),
02846 $url,
02847 $wgLang->timeanddate( $expiration, false ),
02848 $invalidateURL ) );
02849 }
02850
02861 function sendMail( $subject, $body, $from = null, $replyto = null ) {
02862 if( is_null( $from ) ) {
02863 global $wgPasswordSender;
02864 $from = $wgPasswordSender;
02865 }
02866
02867 $to = new MailAddress( $this );
02868 $sender = new MailAddress( $from );
02869 return UserMailer::send( $to, $sender, $subject, $body, $replyto );
02870 }
02871
02883 function confirmationToken( &$expiration ) {
02884 $now = time();
02885 $expires = $now + 7 * 24 * 60 * 60;
02886 $expiration = wfTimestamp( TS_MW, $expires );
02887 $token = $this->generateToken( $this->mId . $this->mEmail . $expires );
02888 $hash = md5( $token );
02889 $this->load();
02890 $this->mEmailToken = $hash;
02891 $this->mEmailTokenExpires = $expiration;
02892 return $token;
02893 }
02894
02901 function confirmationTokenUrl( $token ) {
02902 return $this->getTokenUrl( 'ConfirmEmail', $token );
02903 }
02910 function invalidationTokenUrl( $token ) {
02911 return $this->getTokenUrl( 'Invalidateemail', $token );
02912 }
02913
02928 protected function getTokenUrl( $page, $token ) {
02929 global $wgArticlePath;
02930 return wfExpandUrl(
02931 str_replace(
02932 '$1',
02933 "Special:$page/$token",
02934 $wgArticlePath ) );
02935 }
02936
02942 function confirmEmail() {
02943 $this->setEmailAuthenticationTimestamp( wfTimestampNow() );
02944 return true;
02945 }
02946
02953 function invalidateEmail() {
02954 $this->load();
02955 $this->mEmailToken = null;
02956 $this->mEmailTokenExpires = null;
02957 $this->setEmailAuthenticationTimestamp( null );
02958 return true;
02959 }
02960
02965 function setEmailAuthenticationTimestamp( $timestamp ) {
02966 $this->load();
02967 $this->mEmailAuthenticated = $timestamp;
02968 wfRunHooks( 'UserSetEmailAuthenticationTimestamp', array( $this, &$this->mEmailAuthenticated ) );
02969 }
02970
02976 function canSendEmail() {
02977 global $wgEnableEmail, $wgEnableUserEmail;
02978 if( !$wgEnableEmail || !$wgEnableUserEmail ) {
02979 return false;
02980 }
02981 $canSend = $this->isEmailConfirmed();
02982 wfRunHooks( 'UserCanSendEmail', array( &$this, &$canSend ) );
02983 return $canSend;
02984 }
02985
02991 function canReceiveEmail() {
02992 return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
02993 }
02994
03005 function isEmailConfirmed() {
03006 global $wgEmailAuthentication;
03007 $this->load();
03008 $confirmed = true;
03009 if( wfRunHooks( 'EmailConfirmed', array( &$this, &$confirmed ) ) ) {
03010 if( $this->isAnon() )
03011 return false;
03012 if( !self::isValidEmailAddr( $this->mEmail ) )
03013 return false;
03014 if( $wgEmailAuthentication && !$this->getEmailAuthenticationTimestamp() )
03015 return false;
03016 return true;
03017 } else {
03018 return $confirmed;
03019 }
03020 }
03021
03026 function isEmailConfirmationPending() {
03027 global $wgEmailAuthentication;
03028 return $wgEmailAuthentication &&
03029 !$this->isEmailConfirmed() &&
03030 $this->mEmailToken &&
03031 $this->mEmailTokenExpires > wfTimestamp();
03032 }
03033
03040 public function getRegistration() {
03041 return $this->getId() > 0
03042 ? $this->mRegistration
03043 : false;
03044 }
03045
03052 public function getFirstEditTimestamp() {
03053 if( $this->getId() == 0 ) return false;
03054 $dbr = wfGetDB( DB_SLAVE );
03055 $time = $dbr->selectField( 'revision', 'rev_timestamp',
03056 array( 'rev_user' => $this->getId() ),
03057 __METHOD__,
03058 array( 'ORDER BY' => 'rev_timestamp ASC' )
03059 );
03060 if( !$time ) return false;
03061 return wfTimestamp( TS_MW, $time );
03062 }
03063
03070 static function getGroupPermissions( $groups ) {
03071 global $wgGroupPermissions;
03072 $rights = array();
03073 foreach( $groups as $group ) {
03074 if( isset( $wgGroupPermissions[$group] ) ) {
03075 $rights = array_merge( $rights,
03076
03077 array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
03078 }
03079 }
03080 return array_unique($rights);
03081 }
03082
03089 static function getGroupsWithPermission( $role ) {
03090 global $wgGroupPermissions;
03091 $allowedGroups = array();
03092 foreach ( $wgGroupPermissions as $group => $rights ) {
03093 if ( isset( $rights[$role] ) && $rights[$role] ) {
03094 $allowedGroups[] = $group;
03095 }
03096 }
03097 return $allowedGroups;
03098 }
03099
03106 static function getGroupName( $group ) {
03107 global $wgMessageCache;
03108 $wgMessageCache->loadAllMessages();
03109 $key = "group-$group";
03110 $name = wfMsg( $key );
03111 return $name == '' || wfEmptyMsg( $key, $name )
03112 ? $group
03113 : $name;
03114 }
03115
03122 static function getGroupMember( $group ) {
03123 global $wgMessageCache;
03124 $wgMessageCache->loadAllMessages();
03125 $key = "group-$group-member";
03126 $name = wfMsg( $key );
03127 return $name == '' || wfEmptyMsg( $key, $name )
03128 ? $group
03129 : $name;
03130 }
03131
03138 static function getAllGroups() {
03139 global $wgGroupPermissions;
03140 return array_diff(
03141 array_keys( $wgGroupPermissions ),
03142 self::getImplicitGroups()
03143 );
03144 }
03145
03150 static function getAllRights() {
03151 if ( self::$mAllRights === false ) {
03152 global $wgAvailableRights;
03153 if ( count( $wgAvailableRights ) ) {
03154 self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
03155 } else {
03156 self::$mAllRights = self::$mCoreRights;
03157 }
03158 wfRunHooks( 'UserGetAllRights', array( &self::$mAllRights ) );
03159 }
03160 return self::$mAllRights;
03161 }
03162
03167 public static function getImplicitGroups() {
03168 global $wgImplicitGroups;
03169 $groups = $wgImplicitGroups;
03170 wfRunHooks( 'UserGetImplicitGroups', array( &$groups ) ); #deprecated, use $wgImplictGroups instead
03171 return $groups;
03172 }
03173
03180 static function getGroupPage( $group ) {
03181 global $wgMessageCache;
03182 $wgMessageCache->loadAllMessages();
03183 $page = wfMsgForContent( 'grouppage-' . $group );
03184 if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) {
03185 $title = Title::newFromText( $page );
03186 if( is_object( $title ) )
03187 return $title;
03188 }
03189 return false;
03190 }
03191
03200 static function makeGroupLinkHTML( $group, $text = '' ) {
03201 if( $text == '' ) {
03202 $text = self::getGroupName( $group );
03203 }
03204 $title = self::getGroupPage( $group );
03205 if( $title ) {
03206 global $wgUser;
03207 $sk = $wgUser->getSkin();
03208 return $sk->makeLinkObj( $title, htmlspecialchars( $text ) );
03209 } else {
03210 return $text;
03211 }
03212 }
03213
03222 static function makeGroupLinkWiki( $group, $text = '' ) {
03223 if( $text == '' ) {
03224 $text = self::getGroupName( $group );
03225 }
03226 $title = self::getGroupPage( $group );
03227 if( $title ) {
03228 $page = $title->getPrefixedText();
03229 return "[[$page|$text]]";
03230 } else {
03231 return $text;
03232 }
03233 }
03234
03239 function incEditCount() {
03240 if( !$this->isAnon() ) {
03241 $dbw = wfGetDB( DB_MASTER );
03242 $dbw->update( 'user',
03243 array( 'user_editcount=user_editcount+1' ),
03244 array( 'user_id' => $this->getId() ),
03245 __METHOD__ );
03246
03247
03248 if( $dbw->affectedRows() == 0 ) {
03249
03250
03251 $dbr = wfGetDB( DB_SLAVE );
03252 $count = $dbr->selectField( 'revision',
03253 'COUNT(rev_user)',
03254 array( 'rev_user' => $this->getId() ),
03255 __METHOD__ );
03256
03257
03258 if( $dbr !== $dbw ) {
03259
03260
03261
03262 $count++;
03263 } else {
03264
03265
03266
03267 }
03268
03269 $dbw->update( 'user',
03270 array( 'user_editcount' => $count ),
03271 array( 'user_id' => $this->getId() ),
03272 __METHOD__ );
03273 }
03274 }
03275
03276 $this->invalidateCache();
03277 }
03278
03285 static function getRightDescription( $right ) {
03286 global $wgMessageCache;
03287 $wgMessageCache->loadAllMessages();
03288 $key = "right-$right";
03289 $name = wfMsg( $key );
03290 return $name == '' || wfEmptyMsg( $key, $name )
03291 ? $right
03292 : $name;
03293 }
03294
03302 static function oldCrypt( $password, $userId ) {
03303 global $wgPasswordSalt;
03304 if ( $wgPasswordSalt ) {
03305 return md5( $userId . '-' . md5( $password ) );
03306 } else {
03307 return md5( $password );
03308 }
03309 }
03310
03319 static function crypt( $password, $salt = false ) {
03320 global $wgPasswordSalt;
03321
03322 $hash = '';
03323 if( !wfRunHooks( 'UserCryptPassword', array( &$password, &$salt, &$wgPasswordSalt, &$hash ) ) ) {
03324 return $hash;
03325 }
03326
03327 if( $wgPasswordSalt ) {
03328 if ( $salt === false ) {
03329 $salt = substr( wfGenerateToken(), 0, 8 );
03330 }
03331 return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) );
03332 } else {
03333 return ':A:' . md5( $password );
03334 }
03335 }
03336
03346 static function comparePasswords( $hash, $password, $userId = false ) {
03347 $m = false;
03348 $type = substr( $hash, 0, 3 );
03349
03350 $result = false;
03351 if( !wfRunHooks( 'UserComparePasswords', array( &$hash, &$password, &$userId, &$result ) ) ) {
03352 return $result;
03353 }
03354
03355 if ( $type == ':A:' ) {
03356 # Unsalted
03357 return md5( $password ) === substr( $hash, 3 );
03358 } elseif ( $type == ':B:' ) {
03359 # Salted
03360 list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
03361 return md5( $salt.'-'.md5( $password ) ) == $realHash;
03362 } else {
03363 # Old-style
03364 return self::oldCrypt( $password, $userId ) === $hash;
03365 }
03366 }
03367
03372 public function addNewUserLogEntry( $byEmail = false ) {
03373 global $wgUser, $wgContLang, $wgNewUserLog;
03374 if( empty($wgNewUserLog) ) {
03375 return true;
03376 }
03377 $talk = $wgContLang->getFormattedNsText( NS_TALK );
03378 if( $this->getName() == $wgUser->getName() ) {
03379 $action = 'create';
03380 $message = '';
03381 } else {
03382 $action = 'create2';
03383 $message = $byEmail ? wfMsgForContent( 'newuserlog-byemail' ) : '';
03384 }
03385 $log = new LogPage( 'newusers' );
03386 $log->addEntry( $action, $this->getUserPage(), $message, array( $this->getId() ) );
03387 return true;
03388 }
03389
03394 public function addNewUserLogEntryAutoCreate() {
03395 global $wgNewUserLog;
03396 if( empty($wgNewUserLog) ) {
03397 return true;
03398 }
03399 $log = new LogPage( 'newusers', false );
03400 $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) );
03401 return true;
03402 }
03403
03404 }