MediaWiki
REL1_24
|
00001 <?php 00029 class LoginForm extends SpecialPage { 00030 const SUCCESS = 0; 00031 const NO_NAME = 1; 00032 const ILLEGAL = 2; 00033 const WRONG_PLUGIN_PASS = 3; 00034 const NOT_EXISTS = 4; 00035 const WRONG_PASS = 5; 00036 const EMPTY_PASS = 6; 00037 const RESET_PASS = 7; 00038 const ABORTED = 8; 00039 const CREATE_BLOCKED = 9; 00040 const THROTTLED = 10; 00041 const USER_BLOCKED = 11; 00042 const NEED_TOKEN = 12; 00043 const WRONG_TOKEN = 13; 00044 const USER_MIGRATED = 14; 00045 00059 public static $validErrorMessages = array( 00060 'exception-nologin-text', 00061 'watchlistanontext', 00062 'changeemail-no-info', 00063 'resetpass-no-info', 00064 'confirmemail_needlogin', 00065 'prefsnologintext2', 00066 ); 00067 00068 public $mAbortLoginErrorMsg = null; 00069 00070 protected $mUsername; 00071 protected $mPassword; 00072 protected $mRetype; 00073 protected $mReturnTo; 00074 protected $mCookieCheck; 00075 protected $mPosted; 00076 protected $mAction; 00077 protected $mCreateaccount; 00078 protected $mCreateaccountMail; 00079 protected $mLoginattempt; 00080 protected $mRemember; 00081 protected $mEmail; 00082 protected $mDomain; 00083 protected $mLanguage; 00084 protected $mSkipCookieCheck; 00085 protected $mReturnToQuery; 00086 protected $mToken; 00087 protected $mStickHTTPS; 00088 protected $mType; 00089 protected $mReason; 00090 protected $mRealName; 00091 protected $mEntryError = ''; 00092 protected $mEntryErrorType = 'error'; 00093 00094 private $mTempPasswordUsed; 00095 private $mLoaded = false; 00096 private $mSecureLoginUrl; 00097 00099 private $mOverrideRequest = null; 00100 00102 private $mRequest = null; 00103 00107 public function __construct( $request = null ) { 00108 parent::__construct( 'Userlogin' ); 00109 00110 $this->mOverrideRequest = $request; 00111 } 00112 00116 function load() { 00117 global $wgAuth, $wgHiddenPrefs, $wgEnableEmail; 00118 00119 if ( $this->mLoaded ) { 00120 return; 00121 } 00122 $this->mLoaded = true; 00123 00124 if ( $this->mOverrideRequest === null ) { 00125 $request = $this->getRequest(); 00126 } else { 00127 $request = $this->mOverrideRequest; 00128 } 00129 $this->mRequest = $request; 00130 00131 $this->mType = $request->getText( 'type' ); 00132 $this->mUsername = $request->getText( 'wpName' ); 00133 $this->mPassword = $request->getText( 'wpPassword' ); 00134 $this->mRetype = $request->getText( 'wpRetype' ); 00135 $this->mDomain = $request->getText( 'wpDomain' ); 00136 $this->mReason = $request->getText( 'wpReason' ); 00137 $this->mCookieCheck = $request->getVal( 'wpCookieCheck' ); 00138 $this->mPosted = $request->wasPosted(); 00139 $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' ) 00140 && $wgEnableEmail; 00141 $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ) && !$this->mCreateaccountMail; 00142 $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' ); 00143 $this->mAction = $request->getVal( 'action' ); 00144 $this->mRemember = $request->getCheck( 'wpRemember' ); 00145 $this->mFromHTTP = $request->getBool( 'fromhttp', false ); 00146 $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' ) 00147 || $request->getBool( 'wpForceHttps', false ); 00148 $this->mLanguage = $request->getText( 'uselang' ); 00149 $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' ); 00150 $this->mToken = $this->mType == 'signup' 00151 ? $request->getVal( 'wpCreateaccountToken' ) 00152 : $request->getVal( 'wpLoginToken' ); 00153 $this->mReturnTo = $request->getVal( 'returnto', '' ); 00154 $this->mReturnToQuery = $request->getVal( 'returntoquery', '' ); 00155 00156 // Show an error or warning passed on from a previous page 00157 $entryError = $this->msg( $request->getVal( 'error', '' ) ); 00158 $entryWarning = $this->msg( $request->getVal( 'warning', '' ) ); 00159 // bc: provide login link as a parameter for messages where the translation 00160 // was not updated 00161 $loginreqlink = Linker::linkKnown( 00162 $this->getPageTitle(), 00163 $this->msg( 'loginreqlink' )->escaped(), 00164 array(), 00165 array( 00166 'returnto' => $this->mReturnTo, 00167 'returntoquery' => $this->mReturnToQuery, 00168 'uselang' => $this->mLanguage, 00169 'fromhttp' => $this->mFromHTTP ? '1' : '0', 00170 ) 00171 ); 00172 00173 // Only show valid error or warning messages. 00174 if ( $entryError->exists() 00175 && in_array( $entryError->getKey(), self::$validErrorMessages ) 00176 ) { 00177 $this->mEntryErrorType = 'error'; 00178 $this->mEntryError = $entryError->rawParams( $loginreqlink )->escaped(); 00179 00180 } elseif ( $entryWarning->exists() 00181 && in_array( $entryWarning->getKey(), self::$validErrorMessages ) 00182 ) { 00183 $this->mEntryErrorType = 'warning'; 00184 $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->escaped(); 00185 } 00186 00187 if ( $wgEnableEmail ) { 00188 $this->mEmail = $request->getText( 'wpEmail' ); 00189 } else { 00190 $this->mEmail = ''; 00191 } 00192 if ( !in_array( 'realname', $wgHiddenPrefs ) ) { 00193 $this->mRealName = $request->getText( 'wpRealName' ); 00194 } else { 00195 $this->mRealName = ''; 00196 } 00197 00198 if ( !$wgAuth->validDomain( $this->mDomain ) ) { 00199 $this->mDomain = $wgAuth->getDomain(); 00200 } 00201 $wgAuth->setDomain( $this->mDomain ); 00202 00203 # 1. When switching accounts, it sucks to get automatically logged out 00204 # 2. Do not return to PasswordReset after a successful password change 00205 # but goto Wiki start page (Main_Page) instead ( bug 33997 ) 00206 $returnToTitle = Title::newFromText( $this->mReturnTo ); 00207 if ( is_object( $returnToTitle ) 00208 && ( $returnToTitle->isSpecial( 'Userlogout' ) 00209 || $returnToTitle->isSpecial( 'PasswordReset' ) ) 00210 ) { 00211 $this->mReturnTo = ''; 00212 $this->mReturnToQuery = ''; 00213 } 00214 } 00215 00216 function getDescription() { 00217 if ( $this->mType === 'signup' ) { 00218 return $this->msg( 'createaccount' )->text(); 00219 } else { 00220 return $this->msg( 'login' )->text(); 00221 } 00222 } 00223 00227 public function execute( $subPage ) { 00228 if ( session_id() == '' ) { 00229 wfSetupSession(); 00230 } 00231 00232 $this->load(); 00233 00234 // Check for [[Special:Userlogin/signup]]. This affects form display and 00235 // page title. 00236 if ( $subPage == 'signup' ) { 00237 $this->mType = 'signup'; 00238 } 00239 $this->setHeaders(); 00240 00241 // In the case where the user is already logged in, and was redirected to the login form from a 00242 // page that requires login, do not show the login page. The use case scenario for this is when 00243 // a user opens a large number of tabs, is redirected to the login page on all of them, and then 00244 // logs in on one, expecting all the others to work properly. 00245 // 00246 // However, do show the form if it was visited intentionally (no 'returnto' is present). People 00247 // who often switch between several accounts have grown accustomed to this behavior. 00248 if ( 00249 $this->mType !== 'signup' && 00250 !$this->mPosted && 00251 $this->getUser()->isLoggedIn() && 00252 ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' ) 00253 ) { 00254 $this->successfulLogin(); 00255 } 00256 00257 // If logging in and not on HTTPS, either redirect to it or offer a link. 00258 global $wgSecureLogin; 00259 if ( $this->mRequest->getProtocol() !== 'https' ) { 00260 $title = $this->getFullTitle(); 00261 $query = array( 00262 'returnto' => $this->mReturnTo !== '' ? $this->mReturnTo : null, 00263 'returntoquery' => $this->mReturnToQuery !== '' ? 00264 $this->mReturnToQuery : null, 00265 'title' => null, 00266 ( $this->mEntryErrorType === 'error' ? 'error' : 'warning' ) => $this->mEntryError, 00267 ) + $this->mRequest->getQueryValues(); 00268 $url = $title->getFullURL( $query, false, PROTO_HTTPS ); 00269 if ( $wgSecureLogin 00270 && wfCanIPUseHTTPS( $this->getRequest()->getIP() ) 00271 && !$this->mFromHTTP ) // Avoid infinite redirect 00272 { 00273 $url = wfAppendQuery( $url, 'fromhttp=1' ); 00274 $this->getOutput()->redirect( $url ); 00275 // Since we only do this redir to change proto, always vary 00276 $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' ); 00277 00278 return; 00279 } else { 00280 // A wiki without HTTPS login support should set $wgServer to 00281 // http://somehost, in which case the secure URL generated 00282 // above won't actually start with https:// 00283 if ( substr( $url, 0, 8 ) === 'https://' ) { 00284 $this->mSecureLoginUrl = $url; 00285 } 00286 } 00287 } 00288 00289 if ( !is_null( $this->mCookieCheck ) ) { 00290 $this->onCookieRedirectCheck( $this->mCookieCheck ); 00291 00292 return; 00293 } elseif ( $this->mPosted ) { 00294 if ( $this->mCreateaccount ) { 00295 $this->addNewAccount(); 00296 00297 return; 00298 } elseif ( $this->mCreateaccountMail ) { 00299 $this->addNewAccountMailPassword(); 00300 00301 return; 00302 } elseif ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) { 00303 $this->processLogin(); 00304 00305 return; 00306 } 00307 } 00308 $this->mainLoginForm( $this->mEntryError, $this->mEntryErrorType ); 00309 } 00310 00314 function addNewAccountMailPassword() { 00315 if ( $this->mEmail == '' ) { 00316 $this->mainLoginForm( $this->msg( 'noemailcreate' )->escaped() ); 00317 00318 return; 00319 } 00320 00321 $status = $this->addNewAccountInternal(); 00322 if ( !$status->isGood() ) { 00323 $error = $status->getMessage(); 00324 $this->mainLoginForm( $error->toString() ); 00325 00326 return; 00327 } 00328 00329 $u = $status->getValue(); 00330 00331 // Wipe the initial password and mail a temporary one 00332 $u->setPassword( null ); 00333 $u->saveSettings(); 00334 $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' ); 00335 00336 wfRunHooks( 'AddNewAccount', array( $u, true ) ); 00337 $u->addNewUserLogEntry( 'byemail', $this->mReason ); 00338 00339 $out = $this->getOutput(); 00340 $out->setPageTitle( $this->msg( 'accmailtitle' ) ); 00341 00342 if ( !$result->isGood() ) { 00343 $this->mainLoginForm( $this->msg( 'mailerror', $result->getWikiText() )->text() ); 00344 } else { 00345 $out->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() ); 00346 $this->executeReturnTo( 'success' ); 00347 } 00348 } 00349 00354 function addNewAccount() { 00355 global $wgContLang, $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector; 00356 00357 # Create the account and abort if there's a problem doing so 00358 $status = $this->addNewAccountInternal(); 00359 if ( !$status->isGood() ) { 00360 $error = $status->getMessage(); 00361 $this->mainLoginForm( $error->toString() ); 00362 00363 return false; 00364 } 00365 00366 $u = $status->getValue(); 00367 00368 # Only save preferences if the user is not creating an account for someone else. 00369 if ( $this->getUser()->isAnon() ) { 00370 # If we showed up language selection links, and one was in use, be 00371 # smart (and sensible) and save that language as the user's preference 00372 if ( $wgLoginLanguageSelector && $this->mLanguage ) { 00373 $u->setOption( 'language', $this->mLanguage ); 00374 } else { 00375 00376 # Otherwise the user's language preference defaults to $wgContLang, 00377 # but it may be better to set it to their preferred $wgContLang variant, 00378 # based on browser preferences or URL parameters. 00379 $u->setOption( 'language', $wgContLang->getPreferredVariant() ); 00380 } 00381 if ( $wgContLang->hasVariants() ) { 00382 $u->setOption( 'variant', $wgContLang->getPreferredVariant() ); 00383 } 00384 } 00385 00386 $out = $this->getOutput(); 00387 00388 # Send out an email authentication message if needed 00389 if ( $wgEmailAuthentication && Sanitizer::validateEmail( $u->getEmail() ) ) { 00390 $status = $u->sendConfirmationMail(); 00391 if ( $status->isGood() ) { 00392 $out->addWikiMsg( 'confirmemail_oncreate' ); 00393 } else { 00394 $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) ); 00395 } 00396 } 00397 00398 # Save settings (including confirmation token) 00399 $u->saveSettings(); 00400 00401 # If not logged in, assume the new account as the current one and set 00402 # session cookies then show a "welcome" message or a "need cookies" 00403 # message as needed 00404 if ( $this->getUser()->isAnon() ) { 00405 $u->setCookies(); 00406 $wgUser = $u; 00407 // This should set it for OutputPage and the Skin 00408 // which is needed or the personal links will be 00409 // wrong. 00410 $this->getContext()->setUser( $u ); 00411 wfRunHooks( 'AddNewAccount', array( $u, false ) ); 00412 $u->addNewUserLogEntry( 'create' ); 00413 if ( $this->hasSessionCookie() ) { 00414 $this->successfulCreation(); 00415 } else { 00416 $this->cookieRedirectCheck( 'new' ); 00417 } 00418 } else { 00419 # Confirm that the account was created 00420 $out->setPageTitle( $this->msg( 'accountcreated' ) ); 00421 $out->addWikiMsg( 'accountcreatedtext', $u->getName() ); 00422 $out->addReturnTo( $this->getPageTitle() ); 00423 wfRunHooks( 'AddNewAccount', array( $u, false ) ); 00424 $u->addNewUserLogEntry( 'create2', $this->mReason ); 00425 } 00426 00427 return true; 00428 } 00429 00436 public function addNewAccountInternal() { 00437 global $wgAuth, $wgMemc, $wgAccountCreationThrottle, 00438 $wgMinimalPasswordLength, $wgEmailConfirmToEdit; 00439 00440 // If the user passes an invalid domain, something is fishy 00441 if ( !$wgAuth->validDomain( $this->mDomain ) ) { 00442 return Status::newFatal( 'wrongpassword' ); 00443 } 00444 00445 // If we are not allowing users to login locally, we should be checking 00446 // to see if the user is actually able to authenticate to the authenti- 00447 // cation server before they create an account (otherwise, they can 00448 // create a local account and login as any domain user). We only need 00449 // to check this for domains that aren't local. 00450 if ( 'local' != $this->mDomain && $this->mDomain != '' ) { 00451 if ( 00452 !$wgAuth->canCreateAccounts() && 00453 ( 00454 !$wgAuth->userExists( $this->mUsername ) || 00455 !$wgAuth->authenticate( $this->mUsername, $this->mPassword ) 00456 ) 00457 ) { 00458 return Status::newFatal( 'wrongpassword' ); 00459 } 00460 } 00461 00462 if ( wfReadOnly() ) { 00463 throw new ReadOnlyError; 00464 } 00465 00466 # Request forgery checks. 00467 if ( !self::getCreateaccountToken() ) { 00468 self::setCreateaccountToken(); 00469 00470 return Status::newFatal( 'nocookiesfornew' ); 00471 } 00472 00473 # The user didn't pass a createaccount token 00474 if ( !$this->mToken ) { 00475 return Status::newFatal( 'sessionfailure' ); 00476 } 00477 00478 # Validate the createaccount token 00479 if ( $this->mToken !== self::getCreateaccountToken() ) { 00480 return Status::newFatal( 'sessionfailure' ); 00481 } 00482 00483 # Check permissions 00484 $currentUser = $this->getUser(); 00485 $creationBlock = $currentUser->isBlockedFromCreateAccount(); 00486 if ( !$currentUser->isAllowed( 'createaccount' ) ) { 00487 throw new PermissionsError( 'createaccount' ); 00488 } elseif ( $creationBlock instanceof Block ) { 00489 // Throws an ErrorPageError. 00490 $this->userBlockedMessage( $creationBlock ); 00491 00492 // This should never be reached. 00493 return false; 00494 } 00495 00496 # Include checks that will include GlobalBlocking (Bug 38333) 00497 $permErrors = $this->getPageTitle()->getUserPermissionsErrors( 00498 'createaccount', 00499 $currentUser, 00500 true 00501 ); 00502 00503 if ( count( $permErrors ) ) { 00504 throw new PermissionsError( 'createaccount', $permErrors ); 00505 } 00506 00507 $ip = $this->getRequest()->getIP(); 00508 if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) { 00509 return Status::newFatal( 'sorbs_create_account_reason' ); 00510 } 00511 00512 // Normalize the name so that silly things don't cause "invalid username" 00513 // errors. User::newFromName does some rather strict checking, rejecting 00514 // e.g. leading/trailing/multiple spaces. But first we need to reject 00515 // usernames that would be treated as titles with a fragment part. 00516 if ( strpos( $this->mUsername, '#' ) !== false ) { 00517 return Status::newFatal( 'noname' ); 00518 } 00519 $title = Title::makeTitleSafe( NS_USER, $this->mUsername ); 00520 if ( !is_object( $title ) ) { 00521 return Status::newFatal( 'noname' ); 00522 } 00523 00524 # Now create a dummy user ($u) and check if it is valid 00525 $u = User::newFromName( $title->getText(), 'creatable' ); 00526 if ( !is_object( $u ) ) { 00527 return Status::newFatal( 'noname' ); 00528 } elseif ( 0 != $u->idForName() ) { 00529 return Status::newFatal( 'userexists' ); 00530 } 00531 00532 if ( $this->mCreateaccountMail ) { 00533 # do not force a password for account creation by email 00534 # set invalid password, it will be replaced later by a random generated password 00535 $this->mPassword = null; 00536 } else { 00537 if ( $this->mPassword !== $this->mRetype ) { 00538 return Status::newFatal( 'badretype' ); 00539 } 00540 00541 # check for minimal password length 00542 $valid = $u->getPasswordValidity( $this->mPassword ); 00543 if ( $valid !== true ) { 00544 if ( !is_array( $valid ) ) { 00545 $valid = array( $valid, $wgMinimalPasswordLength ); 00546 } 00547 00548 return call_user_func_array( 'Status::newFatal', $valid ); 00549 } 00550 } 00551 00552 # if you need a confirmed email address to edit, then obviously you 00553 # need an email address. 00554 if ( $wgEmailConfirmToEdit && strval( $this->mEmail ) === '' ) { 00555 return Status::newFatal( 'noemailtitle' ); 00556 } 00557 00558 if ( strval( $this->mEmail ) !== '' && !Sanitizer::validateEmail( $this->mEmail ) ) { 00559 return Status::newFatal( 'invalidemailaddress' ); 00560 } 00561 00562 # Set some additional data so the AbortNewAccount hook can be used for 00563 # more than just username validation 00564 $u->setEmail( $this->mEmail ); 00565 $u->setRealName( $this->mRealName ); 00566 00567 $abortError = ''; 00568 $abortStatus = null; 00569 if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError, &$abortStatus ) ) ) { 00570 // Hook point to add extra creation throttles and blocks 00571 wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" ); 00572 if ( $abortStatus === null ) { 00573 // Report back the old string as a raw message status. 00574 // This will report the error back as 'createaccount-hook-aborted' 00575 // with the given string as the message. 00576 // To return a different error code, return a Status object. 00577 $abortError = new Message( 'createaccount-hook-aborted', array( $abortError ) ); 00578 $abortError->text(); 00579 00580 return Status::newFatal( $abortError ); 00581 } else { 00582 // For MediaWiki 1.23+ and updated hooks, return the Status object 00583 // returned from the hook. 00584 return $abortStatus; 00585 } 00586 } 00587 00588 // Hook point to check for exempt from account creation throttle 00589 if ( !wfRunHooks( 'ExemptFromAccountCreationThrottle', array( $ip ) ) ) { 00590 wfDebug( "LoginForm::exemptFromAccountCreationThrottle: a hook " . 00591 "allowed account creation w/o throttle\n" ); 00592 } else { 00593 if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) { 00594 $key = wfMemcKey( 'acctcreate', 'ip', $ip ); 00595 $value = $wgMemc->get( $key ); 00596 if ( !$value ) { 00597 $wgMemc->set( $key, 0, 86400 ); 00598 } 00599 if ( $value >= $wgAccountCreationThrottle ) { 00600 return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle ); 00601 } 00602 $wgMemc->incr( $key ); 00603 } 00604 } 00605 00606 if ( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) { 00607 return Status::newFatal( 'externaldberror' ); 00608 } 00609 00610 self::clearCreateaccountToken(); 00611 00612 return $this->initUser( $u, false ); 00613 } 00614 00624 function initUser( $u, $autocreate ) { 00625 global $wgAuth; 00626 00627 $status = $u->addToDatabase(); 00628 if ( !$status->isOK() ) { 00629 return $status; 00630 } 00631 00632 if ( $wgAuth->allowPasswordChange() ) { 00633 $u->setPassword( $this->mPassword ); 00634 } 00635 00636 $u->setEmail( $this->mEmail ); 00637 $u->setRealName( $this->mRealName ); 00638 $u->setToken(); 00639 00640 $wgAuth->initUser( $u, $autocreate ); 00641 00642 $u->saveSettings(); 00643 00644 // Update user count 00645 DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) ); 00646 00647 // Watch user's userpage and talk page 00648 $u->addWatch( $u->getUserPage(), WatchedItem::IGNORE_USER_RIGHTS ); 00649 00650 return Status::newGood( $u ); 00651 } 00652 00661 public function authenticateUserData() { 00662 global $wgUser, $wgAuth; 00663 00664 $this->load(); 00665 00666 if ( $this->mUsername == '' ) { 00667 return self::NO_NAME; 00668 } 00669 00670 // We require a login token to prevent login CSRF 00671 // Handle part of this before incrementing the throttle so 00672 // token-less login attempts don't count towards the throttle 00673 // but wrong-token attempts do. 00674 00675 // If the user doesn't have a login token yet, set one. 00676 if ( !self::getLoginToken() ) { 00677 self::setLoginToken(); 00678 00679 return self::NEED_TOKEN; 00680 } 00681 // If the user didn't pass a login token, tell them we need one 00682 if ( !$this->mToken ) { 00683 return self::NEED_TOKEN; 00684 } 00685 00686 $throttleCount = self::incLoginThrottle( $this->mUsername ); 00687 if ( $throttleCount === true ) { 00688 return self::THROTTLED; 00689 } 00690 00691 // Validate the login token 00692 if ( $this->mToken !== self::getLoginToken() ) { 00693 return self::WRONG_TOKEN; 00694 } 00695 00696 // Load the current user now, and check to see if we're logging in as 00697 // the same name. This is necessary because loading the current user 00698 // (say by calling getName()) calls the UserLoadFromSession hook, which 00699 // potentially creates the user in the database. Until we load $wgUser, 00700 // checking for user existence using User::newFromName($name)->getId() below 00701 // will effectively be using stale data. 00702 if ( $this->getUser()->getName() === $this->mUsername ) { 00703 wfDebug( __METHOD__ . ": already logged in as {$this->mUsername}\n" ); 00704 00705 return self::SUCCESS; 00706 } 00707 00708 $u = User::newFromName( $this->mUsername ); 00709 00710 // Give extensions a way to indicate the username has been updated, 00711 // rather than telling the user the account doesn't exist. 00712 if ( !wfRunHooks( 'LoginUserMigrated', array( $u, &$msg ) ) ) { 00713 $this->mAbortLoginErrorMsg = $msg; 00714 return self::USER_MIGRATED; 00715 } 00716 00717 if ( !( $u instanceof User ) || !User::isUsableName( $u->getName() ) ) { 00718 return self::ILLEGAL; 00719 } 00720 00721 $isAutoCreated = false; 00722 if ( $u->getID() == 0 ) { 00723 $status = $this->attemptAutoCreate( $u ); 00724 if ( $status !== self::SUCCESS ) { 00725 return $status; 00726 } else { 00727 $isAutoCreated = true; 00728 } 00729 } else { 00730 $u->load(); 00731 } 00732 00733 // Give general extensions, such as a captcha, a chance to abort logins 00734 $abort = self::ABORTED; 00735 $msg = null; 00736 if ( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) { 00737 $this->mAbortLoginErrorMsg = $msg; 00738 00739 return $abort; 00740 } 00741 00742 global $wgBlockDisablesLogin; 00743 if ( !$u->checkPassword( $this->mPassword ) ) { 00744 if ( $u->checkTemporaryPassword( $this->mPassword ) ) { 00745 // The e-mailed temporary password should not be used for actu- 00746 // al logins; that's a very sloppy habit, and insecure if an 00747 // attacker has a few seconds to click "search" on someone's o- 00748 // pen mail reader. 00749 // 00750 // Allow it to be used only to reset the password a single time 00751 // to a new value, which won't be in the user's e-mail ar- 00752 // chives. 00753 // 00754 // For backwards compatibility, we'll still recognize it at the 00755 // login form to minimize surprises for people who have been 00756 // logging in with a temporary password for some time. 00757 // 00758 // As a side-effect, we can authenticate the user's e-mail ad- 00759 // dress if it's not already done, since the temporary password 00760 // was sent via e-mail. 00761 if ( !$u->isEmailConfirmed() ) { 00762 $u->confirmEmail(); 00763 $u->saveSettings(); 00764 } 00765 00766 // At this point we just return an appropriate code/ indicating 00767 // that the UI should show a password reset form; bot inter- 00768 // faces etc will probably just fail cleanly here. 00769 $this->mAbortLoginErrorMsg = 'resetpass-temp-emailed'; 00770 $this->mTempPasswordUsed = true; 00771 $retval = self::RESET_PASS; 00772 } else { 00773 $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS; 00774 } 00775 } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) { 00776 // If we've enabled it, make it so that a blocked user cannot login 00777 $retval = self::USER_BLOCKED; 00778 } elseif ( $u->getPasswordExpired() == 'hard' ) { 00779 // Force reset now, without logging in 00780 $retval = self::RESET_PASS; 00781 $this->mAbortLoginErrorMsg = 'resetpass-expired'; 00782 } else { 00783 $wgAuth->updateUser( $u ); 00784 $wgUser = $u; 00785 // This should set it for OutputPage and the Skin 00786 // which is needed or the personal links will be 00787 // wrong. 00788 $this->getContext()->setUser( $u ); 00789 00790 // Please reset throttle for successful logins, thanks! 00791 if ( $throttleCount ) { 00792 self::clearLoginThrottle( $this->mUsername ); 00793 } 00794 00795 if ( $isAutoCreated ) { 00796 // Must be run after $wgUser is set, for correct new user log 00797 wfRunHooks( 'AuthPluginAutoCreate', array( $u ) ); 00798 } 00799 00800 $retval = self::SUCCESS; 00801 } 00802 wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) ); 00803 00804 return $retval; 00805 } 00806 00813 public static function incLoginThrottle( $username ) { 00814 global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest; 00815 $username = trim( $username ); // sanity 00816 00817 $throttleCount = 0; 00818 if ( is_array( $wgPasswordAttemptThrottle ) ) { 00819 $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) ); 00820 $count = $wgPasswordAttemptThrottle['count']; 00821 $period = $wgPasswordAttemptThrottle['seconds']; 00822 00823 $throttleCount = $wgMemc->get( $throttleKey ); 00824 if ( !$throttleCount ) { 00825 $wgMemc->add( $throttleKey, 1, $period ); // start counter 00826 } elseif ( $throttleCount < $count ) { 00827 $wgMemc->incr( $throttleKey ); 00828 } elseif ( $throttleCount >= $count ) { 00829 return true; 00830 } 00831 } 00832 00833 return $throttleCount; 00834 } 00835 00841 public static function clearLoginThrottle( $username ) { 00842 global $wgMemc, $wgRequest; 00843 $username = trim( $username ); // sanity 00844 00845 $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) ); 00846 $wgMemc->delete( $throttleKey ); 00847 } 00848 00857 function attemptAutoCreate( $user ) { 00858 global $wgAuth; 00859 00860 if ( $this->getUser()->isBlockedFromCreateAccount() ) { 00861 wfDebug( __METHOD__ . ": user is blocked from account creation\n" ); 00862 00863 return self::CREATE_BLOCKED; 00864 } 00865 00866 if ( !$wgAuth->autoCreate() ) { 00867 return self::NOT_EXISTS; 00868 } 00869 00870 if ( !$wgAuth->userExists( $user->getName() ) ) { 00871 wfDebug( __METHOD__ . ": user does not exist\n" ); 00872 00873 return self::NOT_EXISTS; 00874 } 00875 00876 if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) { 00877 wfDebug( __METHOD__ . ": \$wgAuth->authenticate() returned false, aborting\n" ); 00878 00879 return self::WRONG_PLUGIN_PASS; 00880 } 00881 00882 $abortError = ''; 00883 if ( !wfRunHooks( 'AbortAutoAccount', array( $user, &$abortError ) ) ) { 00884 // Hook point to add extra creation throttles and blocks 00885 wfDebug( "LoginForm::attemptAutoCreate: a hook blocked creation: $abortError\n" ); 00886 $this->mAbortLoginErrorMsg = $abortError; 00887 00888 return self::ABORTED; 00889 } 00890 00891 wfDebug( __METHOD__ . ": creating account\n" ); 00892 $status = $this->initUser( $user, true ); 00893 00894 if ( !$status->isOK() ) { 00895 $errors = $status->getErrorsByType( 'error' ); 00896 $this->mAbortLoginErrorMsg = $errors[0]['message']; 00897 00898 return self::ABORTED; 00899 } 00900 00901 return self::SUCCESS; 00902 } 00903 00904 function processLogin() { 00905 global $wgMemc, $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle, 00906 $wgInvalidPasswordReset; 00907 00908 switch ( $this->authenticateUserData() ) { 00909 case self::SUCCESS: 00910 # We've verified now, update the real record 00911 $user = $this->getUser(); 00912 $user->invalidateCache(); 00913 00914 if ( $user->requiresHTTPS() ) { 00915 $this->mStickHTTPS = true; 00916 } 00917 00918 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 00919 $user->setCookies( $this->mRequest, false, $this->mRemember ); 00920 } else { 00921 $user->setCookies( $this->mRequest, null, $this->mRemember ); 00922 } 00923 self::clearLoginToken(); 00924 00925 // Reset the throttle 00926 $request = $this->getRequest(); 00927 $key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) ); 00928 $wgMemc->delete( $key ); 00929 00930 if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) { 00931 /* Replace the language object to provide user interface in 00932 * correct language immediately on this first page load. 00933 */ 00934 $code = $request->getVal( 'uselang', $user->getOption( 'language' ) ); 00935 $userLang = Language::factory( $code ); 00936 $wgLang = $userLang; 00937 $this->getContext()->setLanguage( $userLang ); 00938 // Reset SessionID on Successful login (bug 40995) 00939 $this->renewSessionId(); 00940 if ( $this->getUser()->getPasswordExpired() == 'soft' ) { 00941 $this->resetLoginForm( $this->msg( 'resetpass-expired-soft' ) ); 00942 } elseif ( $wgInvalidPasswordReset 00943 && !$user->isValidPassword( $this->mPassword ) 00944 ) { 00945 $status = $user->checkPasswordValidity( $this->mPassword ); 00946 $this->resetLoginForm( 00947 $status->getMessage( 'resetpass-validity-soft' ) 00948 ); 00949 } else { 00950 $this->successfulLogin(); 00951 } 00952 } else { 00953 $this->cookieRedirectCheck( 'login' ); 00954 } 00955 break; 00956 00957 case self::NEED_TOKEN: 00958 $error = $this->mAbortLoginErrorMsg ?: 'nocookiesforlogin'; 00959 $this->mainLoginForm( $this->msg( $error )->parse() ); 00960 break; 00961 case self::WRONG_TOKEN: 00962 $error = $this->mAbortLoginErrorMsg ?: 'sessionfailure'; 00963 $this->mainLoginForm( $this->msg( $error )->text() ); 00964 break; 00965 case self::NO_NAME: 00966 case self::ILLEGAL: 00967 $error = $this->mAbortLoginErrorMsg ?: 'noname'; 00968 $this->mainLoginForm( $this->msg( $error )->text() ); 00969 break; 00970 case self::WRONG_PLUGIN_PASS: 00971 $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword'; 00972 $this->mainLoginForm( $this->msg( $error )->text() ); 00973 break; 00974 case self::NOT_EXISTS: 00975 if ( $this->getUser()->isAllowed( 'createaccount' ) ) { 00976 $error = $this->mAbortLoginErrorMsg ?: 'nosuchuser'; 00977 $this->mainLoginForm( $this->msg( $error, 00978 wfEscapeWikiText( $this->mUsername ) )->parse() ); 00979 } else { 00980 $error = $this->mAbortLoginErrorMsg ?: 'nosuchusershort'; 00981 $this->mainLoginForm( $this->msg( $error, 00982 wfEscapeWikiText( $this->mUsername ) )->text() ); 00983 } 00984 break; 00985 case self::WRONG_PASS: 00986 $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword'; 00987 $this->mainLoginForm( $this->msg( $error )->text() ); 00988 break; 00989 case self::EMPTY_PASS: 00990 $error = $this->mAbortLoginErrorMsg ?: 'wrongpasswordempty'; 00991 $this->mainLoginForm( $this->msg( $error )->text() ); 00992 break; 00993 case self::RESET_PASS: 00994 $error = $this->mAbortLoginErrorMsg ?: 'resetpass_announce'; 00995 $this->resetLoginForm( $this->msg( $error ) ); 00996 break; 00997 case self::CREATE_BLOCKED: 00998 $this->userBlockedMessage( $this->getUser()->isBlockedFromCreateAccount() ); 00999 break; 01000 case self::THROTTLED: 01001 $error = $this->mAbortLoginErrorMsg ?: 'login-throttled'; 01002 $this->mainLoginForm( $this->msg( $error ) 01003 ->params( $this->getLanguage()->formatDuration( $wgPasswordAttemptThrottle['seconds'] ) ) 01004 ->text() 01005 ); 01006 break; 01007 case self::USER_BLOCKED: 01008 $error = $this->mAbortLoginErrorMsg ?: 'login-userblocked'; 01009 $this->mainLoginForm( $this->msg( $error, $this->mUsername )->escaped() ); 01010 break; 01011 case self::ABORTED: 01012 $error = $this->mAbortLoginErrorMsg ?: 'login-abort-generic'; 01013 $this->mainLoginForm( $this->msg( $error, 01014 wfEscapeWikiText( $this->mUsername ) )->text() ); 01015 break; 01016 case self::USER_MIGRATED: 01017 $error = $this->mAbortLoginErrorMsg ?: 'login-migrated-generic'; 01018 $params = array(); 01019 if ( is_array( $error ) ) { 01020 $error = array_shift( $this->mAbortLoginErrorMsg ); 01021 $params = $this->mAbortLoginErrorMsg; 01022 } 01023 $this->mainLoginForm( $this->msg( $error, $params )->text() ); 01024 break; 01025 default: 01026 throw new MWException( 'Unhandled case value' ); 01027 } 01028 } 01029 01034 protected function resetLoginForm( Message $msg ) { 01035 // Allow hooks to explain this password reset in more detail 01036 wfRunHooks( 'LoginPasswordResetMessage', array( &$msg, $this->mUsername ) ); 01037 $reset = new SpecialChangePassword(); 01038 $derivative = new DerivativeContext( $this->getContext() ); 01039 $derivative->setTitle( $reset->getPageTitle() ); 01040 $reset->setContext( $derivative ); 01041 if ( !$this->mTempPasswordUsed ) { 01042 $reset->setOldPasswordMessage( 'oldpassword' ); 01043 } 01044 $reset->setChangeMessage( $msg ); 01045 $reset->execute( null ); 01046 } 01047 01055 function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', 01056 $emailText = 'passwordremindertext' 01057 ) { 01058 global $wgNewPasswordExpiry; 01059 01060 if ( $u->getEmail() == '' ) { 01061 return Status::newFatal( 'noemail', $u->getName() ); 01062 } 01063 $ip = $this->getRequest()->getIP(); 01064 if ( !$ip ) { 01065 return Status::newFatal( 'badipaddress' ); 01066 } 01067 01068 $currentUser = $this->getUser(); 01069 wfRunHooks( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) ); 01070 01071 $np = $u->randomPassword(); 01072 $u->setNewpassword( $np, $throttle ); 01073 $u->saveSettings(); 01074 $userLanguage = $u->getOption( 'language' ); 01075 01076 $mainPage = Title::newMainPage(); 01077 $mainPageUrl = $mainPage->getCanonicalURL(); 01078 01079 $m = $this->msg( $emailText, $ip, $u->getName(), $np, '<' . $mainPageUrl . '>', 01080 round( $wgNewPasswordExpiry / 86400 ) )->inLanguage( $userLanguage )->text(); 01081 $result = $u->sendMail( $this->msg( $emailTitle )->inLanguage( $userLanguage )->text(), $m ); 01082 01083 return $result; 01084 } 01085 01096 function successfulLogin() { 01097 # Run any hooks; display injected HTML if any, else redirect 01098 $currentUser = $this->getUser(); 01099 $injected_html = ''; 01100 wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); 01101 01102 if ( $injected_html !== '' ) { 01103 $this->displaySuccessfulAction( 'success', $this->msg( 'loginsuccesstitle' ), 01104 'loginsuccess', $injected_html ); 01105 } else { 01106 $this->executeReturnTo( 'successredirect' ); 01107 } 01108 } 01109 01116 function successfulCreation() { 01117 # Run any hooks; display injected HTML 01118 $currentUser = $this->getUser(); 01119 $injected_html = ''; 01120 $welcome_creation_msg = 'welcomecreation-msg'; 01121 01122 wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); 01123 01129 wfRunHooks( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) ); 01130 01131 $this->displaySuccessfulAction( 01132 'signup', 01133 $this->msg( 'welcomeuser', $this->getUser()->getName() ), 01134 $welcome_creation_msg, $injected_html 01135 ); 01136 } 01137 01146 private function displaySuccessfulAction( $type, $title, $msgname, $injected_html ) { 01147 $out = $this->getOutput(); 01148 $out->setPageTitle( $title ); 01149 if ( $msgname ) { 01150 $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) ); 01151 } 01152 01153 $out->addHTML( $injected_html ); 01154 01155 $this->executeReturnTo( $type ); 01156 } 01157 01166 function userBlockedMessage( Block $block ) { 01167 # Let's be nice about this, it's likely that this feature will be used 01168 # for blocking large numbers of innocent people, e.g. range blocks on 01169 # schools. Don't blame it on the user. There's a small chance that it 01170 # really is the user's fault, i.e. the username is blocked and they 01171 # haven't bothered to log out before trying to create an account to 01172 # evade it, but we'll leave that to their guilty conscience to figure 01173 # out. 01174 $errorParams = array( 01175 $block->getTarget(), 01176 $block->mReason ? $block->mReason : $this->msg( 'blockednoreason' )->text(), 01177 $block->getByName() 01178 ); 01179 01180 if ( $block->getType() === Block::TYPE_RANGE ) { 01181 $errorMessage = 'cantcreateaccount-range-text'; 01182 $errorParams[] = $this->getRequest()->getIP(); 01183 } else { 01184 $errorMessage = 'cantcreateaccount-text'; 01185 } 01186 01187 throw new ErrorPageError( 01188 'cantcreateaccounttitle', 01189 $errorMessage, 01190 $errorParams 01191 ); 01192 } 01193 01209 public function showReturnToPage( 01210 $type, $returnTo = '', $returnToQuery = '', $stickHTTPs = false 01211 ) { 01212 $this->mReturnTo = $returnTo; 01213 $this->mReturnToQuery = $returnToQuery; 01214 $this->mStickHTTPS = $stickHTTPs; 01215 $this->executeReturnTo( $type ); 01216 } 01217 01227 private function executeReturnTo( $type ) { 01228 global $wgRedirectOnLogin, $wgSecureLogin; 01229 01230 if ( $type != 'error' && $wgRedirectOnLogin !== null ) { 01231 $returnTo = $wgRedirectOnLogin; 01232 $returnToQuery = array(); 01233 } else { 01234 $returnTo = $this->mReturnTo; 01235 $returnToQuery = wfCgiToArray( $this->mReturnToQuery ); 01236 } 01237 01238 // Allow modification of redirect behavior 01239 wfRunHooks( 'PostLoginRedirect', array( &$returnTo, &$returnToQuery, &$type ) ); 01240 01241 $returnToTitle = Title::newFromText( $returnTo ); 01242 if ( !$returnToTitle ) { 01243 $returnToTitle = Title::newMainPage(); 01244 } 01245 01246 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 01247 $options = array( 'http' ); 01248 $proto = PROTO_HTTP; 01249 } elseif ( $wgSecureLogin ) { 01250 $options = array( 'https' ); 01251 $proto = PROTO_HTTPS; 01252 } else { 01253 $options = array(); 01254 $proto = PROTO_RELATIVE; 01255 } 01256 01257 if ( $type == 'successredirect' ) { 01258 $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto ); 01259 $this->getOutput()->redirect( $redirectUrl ); 01260 } else { 01261 $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options ); 01262 } 01263 } 01264 01270 function mainLoginForm( $msg, $msgtype = 'error' ) { 01271 global $wgEnableEmail, $wgEnableUserEmail; 01272 global $wgHiddenPrefs, $wgLoginLanguageSelector; 01273 global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration; 01274 global $wgSecureLogin, $wgPasswordResetRoutes; 01275 01276 $titleObj = $this->getPageTitle(); 01277 $user = $this->getUser(); 01278 $out = $this->getOutput(); 01279 01280 if ( $this->mType == 'signup' ) { 01281 // Block signup here if in readonly. Keeps user from 01282 // going through the process (filling out data, etc) 01283 // and being informed later. 01284 $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $user, true ); 01285 if ( count( $permErrors ) ) { 01286 throw new PermissionsError( 'createaccount', $permErrors ); 01287 } elseif ( $user->isBlockedFromCreateAccount() ) { 01288 $this->userBlockedMessage( $user->isBlockedFromCreateAccount() ); 01289 01290 return; 01291 } elseif ( wfReadOnly() ) { 01292 throw new ReadOnlyError; 01293 } 01294 } 01295 01296 // Pre-fill username (if not creating an account, bug 44775). 01297 if ( $this->mUsername == '' && $this->mType != 'signup' ) { 01298 if ( $user->isLoggedIn() ) { 01299 $this->mUsername = $user->getName(); 01300 } else { 01301 $this->mUsername = $this->getRequest()->getCookie( 'UserName' ); 01302 } 01303 } 01304 01305 // Generic styles and scripts for both login and signup form 01306 $out->addModuleStyles( array( 01307 'mediawiki.ui', 01308 'mediawiki.ui.button', 01309 'mediawiki.ui.checkbox', 01310 'mediawiki.ui.input', 01311 'mediawiki.special.userlogin.common.styles' 01312 ) ); 01313 $out->addModules( array( 01314 'mediawiki.special.userlogin.common.js' 01315 ) ); 01316 01317 if ( $this->mType == 'signup' ) { 01318 // XXX hack pending RL or JS parse() support for complex content messages 01319 // https://bugzilla.wikimedia.org/show_bug.cgi?id=25349 01320 $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp', 01321 $this->msg( 'createacct-imgcaptcha-help' )->parse() ); 01322 01323 // Additional styles and scripts for signup form 01324 $out->addModules( array( 01325 'mediawiki.special.userlogin.signup.js' 01326 ) ); 01327 $out->addModuleStyles( array( 01328 'mediawiki.special.userlogin.signup.styles' 01329 ) ); 01330 01331 $template = new UsercreateTemplate(); 01332 01333 // Must match number of benefits defined in messages 01334 $template->set( 'benefitCount', 3 ); 01335 01336 $q = 'action=submitlogin&type=signup'; 01337 $linkq = 'type=login'; 01338 } else { 01339 // Additional styles for login form 01340 $out->addModuleStyles( array( 01341 'mediawiki.special.userlogin.login.styles' 01342 ) ); 01343 01344 $template = new UserloginTemplate(); 01345 01346 $q = 'action=submitlogin&type=login'; 01347 $linkq = 'type=signup'; 01348 } 01349 01350 if ( $this->mReturnTo !== '' ) { 01351 $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo ); 01352 if ( $this->mReturnToQuery !== '' ) { 01353 $returnto .= '&returntoquery=' . 01354 wfUrlencode( $this->mReturnToQuery ); 01355 } 01356 $q .= $returnto; 01357 $linkq .= $returnto; 01358 } 01359 01360 # Don't show a "create account" link if the user can't. 01361 if ( $this->showCreateOrLoginLink( $user ) ) { 01362 # Pass any language selection on to the mode switch link 01363 if ( $wgLoginLanguageSelector && $this->mLanguage ) { 01364 $linkq .= '&uselang=' . $this->mLanguage; 01365 } 01366 // Supply URL, login template creates the button. 01367 $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) ); 01368 } else { 01369 $template->set( 'link', '' ); 01370 } 01371 01372 $resetLink = $this->mType == 'signup' 01373 ? null 01374 : is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) ); 01375 01376 $template->set( 'header', '' ); 01377 $template->set( 'skin', $this->getSkin() ); 01378 $template->set( 'name', $this->mUsername ); 01379 $template->set( 'password', $this->mPassword ); 01380 $template->set( 'retype', $this->mRetype ); 01381 $template->set( 'createemailset', $this->mCreateaccountMail ); 01382 $template->set( 'email', $this->mEmail ); 01383 $template->set( 'realname', $this->mRealName ); 01384 $template->set( 'domain', $this->mDomain ); 01385 $template->set( 'reason', $this->mReason ); 01386 01387 $template->set( 'action', $titleObj->getLocalURL( $q ) ); 01388 $template->set( 'message', $msg ); 01389 $template->set( 'messagetype', $msgtype ); 01390 $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() ); 01391 $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) ); 01392 $template->set( 'useemail', $wgEnableEmail ); 01393 $template->set( 'emailrequired', $wgEmailConfirmToEdit ); 01394 $template->set( 'emailothers', $wgEnableUserEmail ); 01395 $template->set( 'canreset', $wgAuth->allowPasswordChange() ); 01396 $template->set( 'resetlink', $resetLink ); 01397 $template->set( 'canremember', ( $wgCookieExpiration > 0 ) ); 01398 $template->set( 'usereason', $user->isLoggedIn() ); 01399 $template->set( 'remember', $this->mRemember ); 01400 $template->set( 'cansecurelogin', ( $wgSecureLogin === true ) ); 01401 $template->set( 'stickhttps', (int)$this->mStickHTTPS ); 01402 $template->set( 'loggedin', $user->isLoggedIn() ); 01403 $template->set( 'loggedinuser', $user->getName() ); 01404 01405 if ( $this->mType == 'signup' ) { 01406 if ( !self::getCreateaccountToken() ) { 01407 self::setCreateaccountToken(); 01408 } 01409 $template->set( 'token', self::getCreateaccountToken() ); 01410 } else { 01411 if ( !self::getLoginToken() ) { 01412 self::setLoginToken(); 01413 } 01414 $template->set( 'token', self::getLoginToken() ); 01415 } 01416 01417 # Prepare language selection links as needed 01418 if ( $wgLoginLanguageSelector ) { 01419 $template->set( 'languages', $this->makeLanguageSelector() ); 01420 if ( $this->mLanguage ) { 01421 $template->set( 'uselang', $this->mLanguage ); 01422 } 01423 } 01424 01425 $template->set( 'secureLoginUrl', $this->mSecureLoginUrl ); 01426 // Use loginend-https for HTTPS requests if it's not blank, loginend otherwise 01427 // Ditto for signupend. New forms use neither. 01428 $usingHTTPS = $this->mRequest->getProtocol() == 'https'; 01429 $loginendHTTPS = $this->msg( 'loginend-https' ); 01430 $signupendHTTPS = $this->msg( 'signupend-https' ); 01431 if ( $usingHTTPS && !$loginendHTTPS->isBlank() ) { 01432 $template->set( 'loginend', $loginendHTTPS->parse() ); 01433 } else { 01434 $template->set( 'loginend', $this->msg( 'loginend' )->parse() ); 01435 } 01436 if ( $usingHTTPS && !$signupendHTTPS->isBlank() ) { 01437 $template->set( 'signupend', $signupendHTTPS->parse() ); 01438 } else { 01439 $template->set( 'signupend', $this->msg( 'signupend' )->parse() ); 01440 } 01441 01442 // Give authentication and captcha plugins a chance to modify the form 01443 $wgAuth->modifyUITemplate( $template, $this->mType ); 01444 if ( $this->mType == 'signup' ) { 01445 wfRunHooks( 'UserCreateForm', array( &$template ) ); 01446 } else { 01447 wfRunHooks( 'UserLoginForm', array( &$template ) ); 01448 } 01449 01450 $out->disallowUserJs(); // just in case... 01451 $out->addTemplate( $template ); 01452 } 01453 01461 private function showCreateOrLoginLink( &$user ) { 01462 if ( $this->mType == 'signup' ) { 01463 return true; 01464 } elseif ( $user->isAllowed( 'createaccount' ) ) { 01465 return true; 01466 } else { 01467 return false; 01468 } 01469 } 01470 01481 function hasSessionCookie() { 01482 global $wgDisableCookieCheck; 01483 01484 return $wgDisableCookieCheck ? true : $this->getRequest()->checkSessionCookie(); 01485 } 01486 01491 public static function getLoginToken() { 01492 global $wgRequest; 01493 01494 return $wgRequest->getSessionData( 'wsLoginToken' ); 01495 } 01496 01500 public static function setLoginToken() { 01501 global $wgRequest; 01502 // Generate a token directly instead of using $user->getEditToken() 01503 // because the latter reuses $_SESSION['wsEditToken'] 01504 $wgRequest->setSessionData( 'wsLoginToken', MWCryptRand::generateHex( 32 ) ); 01505 } 01506 01510 public static function clearLoginToken() { 01511 global $wgRequest; 01512 $wgRequest->setSessionData( 'wsLoginToken', null ); 01513 } 01514 01519 public static function getCreateaccountToken() { 01520 global $wgRequest; 01521 01522 return $wgRequest->getSessionData( 'wsCreateaccountToken' ); 01523 } 01524 01528 public static function setCreateaccountToken() { 01529 global $wgRequest; 01530 $wgRequest->setSessionData( 'wsCreateaccountToken', MWCryptRand::generateHex( 32 ) ); 01531 } 01532 01536 public static function clearCreateaccountToken() { 01537 global $wgRequest; 01538 $wgRequest->setSessionData( 'wsCreateaccountToken', null ); 01539 } 01540 01544 private function renewSessionId() { 01545 global $wgSecureLogin, $wgCookieSecure; 01546 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 01547 $wgCookieSecure = false; 01548 } 01549 01550 wfResetSessionID(); 01551 } 01552 01557 function cookieRedirectCheck( $type ) { 01558 $titleObj = SpecialPage::getTitleFor( 'Userlogin' ); 01559 $query = array( 'wpCookieCheck' => $type ); 01560 if ( $this->mReturnTo !== '' ) { 01561 $query['returnto'] = $this->mReturnTo; 01562 $query['returntoquery'] = $this->mReturnToQuery; 01563 } 01564 $check = $titleObj->getFullURL( $query ); 01565 01566 $this->getOutput()->redirect( $check ); 01567 } 01568 01573 function onCookieRedirectCheck( $type ) { 01574 if ( !$this->hasSessionCookie() ) { 01575 if ( $type == 'new' ) { 01576 $this->mainLoginForm( $this->msg( 'nocookiesnew' )->parse() ); 01577 } elseif ( $type == 'login' ) { 01578 $this->mainLoginForm( $this->msg( 'nocookieslogin' )->parse() ); 01579 } else { 01580 # shouldn't happen 01581 $this->mainLoginForm( $this->msg( 'error' )->text() ); 01582 } 01583 } else { 01584 $this->successfulLogin(); 01585 } 01586 } 01587 01594 function makeLanguageSelector() { 01595 $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage(); 01596 if ( !$msg->isBlank() ) { 01597 $langs = explode( "\n", $msg->text() ); 01598 $links = array(); 01599 foreach ( $langs as $lang ) { 01600 $lang = trim( $lang, '* ' ); 01601 $parts = explode( '|', $lang ); 01602 if ( count( $parts ) >= 2 ) { 01603 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) ); 01604 } 01605 } 01606 01607 return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams( 01608 $this->getLanguage()->pipeList( $links ) )->escaped() : ''; 01609 } else { 01610 return ''; 01611 } 01612 } 01613 01622 function makeLanguageSelectorLink( $text, $lang ) { 01623 if ( $this->getLanguage()->getCode() == $lang ) { 01624 // no link for currently used language 01625 return htmlspecialchars( $text ); 01626 } 01627 $query = array( 'uselang' => $lang ); 01628 if ( $this->mType == 'signup' ) { 01629 $query['type'] = 'signup'; 01630 } 01631 if ( $this->mReturnTo !== '' ) { 01632 $query['returnto'] = $this->mReturnTo; 01633 $query['returntoquery'] = $this->mReturnToQuery; 01634 } 01635 01636 $attr = array(); 01637 $targetLanguage = Language::factory( $lang ); 01638 $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode(); 01639 01640 return Linker::linkKnown( 01641 $this->getPageTitle(), 01642 htmlspecialchars( $text ), 01643 $attr, 01644 $query 01645 ); 01646 } 01647 01648 protected function getGroupName() { 01649 return 'login'; 01650 } 01651 }