MediaWiki
REL1_22
|
00001 <?php 00029 class LoginForm extends SpecialPage { 00030 00031 const SUCCESS = 0; 00032 const NO_NAME = 1; 00033 const ILLEGAL = 2; 00034 const WRONG_PLUGIN_PASS = 3; 00035 const NOT_EXISTS = 4; 00036 const WRONG_PASS = 5; 00037 const EMPTY_PASS = 6; 00038 const RESET_PASS = 7; 00039 const ABORTED = 8; 00040 const CREATE_BLOCKED = 9; 00041 const THROTTLED = 10; 00042 const USER_BLOCKED = 11; 00043 const NEED_TOKEN = 12; 00044 const WRONG_TOKEN = 13; 00045 00046 var $mUsername, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted; 00047 var $mAction, $mCreateaccount, $mCreateaccountMail; 00048 var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage; 00049 var $mSkipCookieCheck, $mReturnToQuery, $mToken, $mStickHTTPS; 00050 var $mType, $mReason, $mRealName; 00051 var $mAbortLoginErrorMsg = null; 00052 private $mLoaded = false; 00053 private $mSecureLoginUrl; 00054 00058 private $mOverrideRequest = null; 00059 00065 private $mRequest = null; 00066 00070 public function __construct( $request = null ) { 00071 parent::__construct( 'Userlogin' ); 00072 00073 $this->mOverrideRequest = $request; 00074 } 00075 00079 function load() { 00080 global $wgAuth, $wgHiddenPrefs, $wgEnableEmail; 00081 00082 if ( $this->mLoaded ) { 00083 return; 00084 } 00085 $this->mLoaded = true; 00086 00087 if ( $this->mOverrideRequest === null ) { 00088 $request = $this->getRequest(); 00089 } else { 00090 $request = $this->mOverrideRequest; 00091 } 00092 $this->mRequest = $request; 00093 00094 $this->mType = $request->getText( 'type' ); 00095 $this->mUsername = $request->getText( 'wpName' ); 00096 $this->mPassword = $request->getText( 'wpPassword' ); 00097 $this->mRetype = $request->getText( 'wpRetype' ); 00098 $this->mDomain = $request->getText( 'wpDomain' ); 00099 $this->mReason = $request->getText( 'wpReason' ); 00100 $this->mCookieCheck = $request->getVal( 'wpCookieCheck' ); 00101 $this->mPosted = $request->wasPosted(); 00102 $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' ) 00103 && $wgEnableEmail; 00104 $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ) && !$this->mCreateaccountMail; 00105 $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' ); 00106 $this->mAction = $request->getVal( 'action' ); 00107 $this->mRemember = $request->getCheck( 'wpRemember' ); 00108 $this->mFromHTTP = $request->getBool( 'fromhttp', false ); 00109 $this->mStickHTTPS = ( !$this->mFromHTTP && $request->detectProtocol() === 'https' ) || $request->getBool( 'wpForceHttps', false ); 00110 $this->mLanguage = $request->getText( 'uselang' ); 00111 $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' ); 00112 $this->mToken = ( $this->mType == 'signup' ) ? $request->getVal( 'wpCreateaccountToken' ) : $request->getVal( 'wpLoginToken' ); 00113 $this->mReturnTo = $request->getVal( 'returnto', '' ); 00114 $this->mReturnToQuery = $request->getVal( 'returntoquery', '' ); 00115 00116 if ( $wgEnableEmail ) { 00117 $this->mEmail = $request->getText( 'wpEmail' ); 00118 } else { 00119 $this->mEmail = ''; 00120 } 00121 if ( !in_array( 'realname', $wgHiddenPrefs ) ) { 00122 $this->mRealName = $request->getText( 'wpRealName' ); 00123 } else { 00124 $this->mRealName = ''; 00125 } 00126 00127 if ( !$wgAuth->validDomain( $this->mDomain ) ) { 00128 $this->mDomain = $wgAuth->getDomain(); 00129 } 00130 $wgAuth->setDomain( $this->mDomain ); 00131 00132 # 1. When switching accounts, it sucks to get automatically logged out 00133 # 2. Do not return to PasswordReset after a successful password change 00134 # but goto Wiki start page (Main_Page) instead ( bug 33997 ) 00135 $returnToTitle = Title::newFromText( $this->mReturnTo ); 00136 if ( is_object( $returnToTitle ) && ( 00137 $returnToTitle->isSpecial( 'Userlogout' ) 00138 || $returnToTitle->isSpecial( 'PasswordReset' ) ) ) { 00139 $this->mReturnTo = ''; 00140 $this->mReturnToQuery = ''; 00141 } 00142 } 00143 00144 function getDescription() { 00145 if ( $this->mType === 'signup' ) { 00146 return $this->msg( 'createaccount' )->text(); 00147 } else { 00148 return $this->msg( 'login' )->text(); 00149 } 00150 } 00151 00152 /* 00153 * @param $subPage string|null 00154 */ 00155 public function execute( $subPage ) { 00156 if ( session_id() == '' ) { 00157 wfSetupSession(); 00158 } 00159 00160 $this->load(); 00161 00162 // Check for [[Special:Userlogin/signup]]. This affects form display and 00163 // page title. 00164 if ( $subPage == 'signup' ) { 00165 $this->mType = 'signup'; 00166 } 00167 $this->setHeaders(); 00168 00169 // If logging in and not on HTTPS, either redirect to it or offer a link. 00170 global $wgSecureLogin; 00171 if ( WebRequest::detectProtocol() !== 'https' ) { 00172 $title = $this->getFullTitle(); 00173 $query = array( 00174 'returnto' => $this->mReturnTo, 00175 'returntoquery' => $this->mReturnToQuery, 00176 'title' => null, 00177 ) + $this->mRequest->getQueryValues(); 00178 $url = $title->getFullURL( $query, false, PROTO_HTTPS ); 00179 if ( $wgSecureLogin && wfCanIPUseHTTPS( $this->getRequest()->getIP() ) ) { 00180 $url = wfAppendQuery( $url, 'fromhttp=1' ); 00181 $this->getOutput()->redirect( $url ); 00182 // Since we only do this redir to change proto, always vary 00183 $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' ); 00184 return; 00185 } else { 00186 // A wiki without HTTPS login support should set $wgServer to 00187 // http://somehost, in which case the secure URL generated 00188 // above won't actually start with https:// 00189 if ( substr( $url, 0, 8 ) === 'https://' ) { 00190 $this->mSecureLoginUrl = $url; 00191 } 00192 } 00193 } 00194 00195 if ( !is_null( $this->mCookieCheck ) ) { 00196 $this->onCookieRedirectCheck( $this->mCookieCheck ); 00197 return; 00198 } elseif ( $this->mPosted ) { 00199 if ( $this->mCreateaccount ) { 00200 $this->addNewAccount(); 00201 return; 00202 } elseif ( $this->mCreateaccountMail ) { 00203 $this->addNewAccountMailPassword(); 00204 return; 00205 } elseif ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) { 00206 $this->processLogin(); 00207 return; 00208 } 00209 } 00210 $this->mainLoginForm( '' ); 00211 } 00212 00216 function addNewAccountMailPassword() { 00217 if ( $this->mEmail == '' ) { 00218 $this->mainLoginForm( $this->msg( 'noemailcreate' )->escaped() ); 00219 return; 00220 } 00221 00222 $status = $this->addNewaccountInternal(); 00223 if ( !$status->isGood() ) { 00224 $error = $status->getMessage(); 00225 $this->mainLoginForm( $error->toString() ); 00226 return; 00227 } 00228 00229 $u = $status->getValue(); 00230 00231 // Wipe the initial password and mail a temporary one 00232 $u->setPassword( null ); 00233 $u->saveSettings(); 00234 $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' ); 00235 00236 wfRunHooks( 'AddNewAccount', array( $u, true ) ); 00237 $u->addNewUserLogEntry( 'byemail', $this->mReason ); 00238 00239 $out = $this->getOutput(); 00240 $out->setPageTitle( $this->msg( 'accmailtitle' ) ); 00241 00242 if ( !$result->isGood() ) { 00243 $this->mainLoginForm( $this->msg( 'mailerror', $result->getWikiText() )->text() ); 00244 } else { 00245 $out->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() ); 00246 $this->executeReturnTo( 'success' ); 00247 } 00248 } 00249 00254 function addNewAccount() { 00255 global $wgContLang, $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector; 00256 00257 # Create the account and abort if there's a problem doing so 00258 $status = $this->addNewAccountInternal(); 00259 if ( !$status->isGood() ) { 00260 $error = $status->getMessage(); 00261 $this->mainLoginForm( $error->toString() ); 00262 return false; 00263 } 00264 00265 $u = $status->getValue(); 00266 00267 # Only save preferences if the user is not creating an account for someone else. 00268 if ( $this->getUser()->isAnon() ) { 00269 # If we showed up language selection links, and one was in use, be 00270 # smart (and sensible) and save that language as the user's preference 00271 if ( $wgLoginLanguageSelector && $this->mLanguage ) { 00272 $u->setOption( 'language', $this->mLanguage ); 00273 } else { 00274 00275 # Otherwise the user's language preference defaults to $wgContLang, 00276 # but it may be better to set it to their preferred $wgContLang variant, 00277 # based on browser preferences or URL parameters. 00278 $u->setOption( 'language', $wgContLang->getPreferredVariant() ); 00279 } 00280 if ( $wgContLang->hasVariants() ) { 00281 $u->setOption( 'variant', $wgContLang->getPreferredVariant() ); 00282 } 00283 } 00284 00285 $out = $this->getOutput(); 00286 00287 # Send out an email authentication message if needed 00288 if ( $wgEmailAuthentication && Sanitizer::validateEmail( $u->getEmail() ) ) { 00289 $status = $u->sendConfirmationMail(); 00290 if ( $status->isGood() ) { 00291 $out->addWikiMsg( 'confirmemail_oncreate' ); 00292 } else { 00293 $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) ); 00294 } 00295 } 00296 00297 # Save settings (including confirmation token) 00298 $u->saveSettings(); 00299 00300 # If not logged in, assume the new account as the current one and set 00301 # session cookies then show a "welcome" message or a "need cookies" 00302 # message as needed 00303 if ( $this->getUser()->isAnon() ) { 00304 $u->setCookies(); 00305 $wgUser = $u; 00306 // This should set it for OutputPage and the Skin 00307 // which is needed or the personal links will be 00308 // wrong. 00309 $this->getContext()->setUser( $u ); 00310 wfRunHooks( 'AddNewAccount', array( $u, false ) ); 00311 $u->addNewUserLogEntry( 'create' ); 00312 if ( $this->hasSessionCookie() ) { 00313 $this->successfulCreation(); 00314 } else { 00315 $this->cookieRedirectCheck( 'new' ); 00316 } 00317 } else { 00318 # Confirm that the account was created 00319 $out->setPageTitle( $this->msg( 'accountcreated' ) ); 00320 $out->addWikiMsg( 'accountcreatedtext', $u->getName() ); 00321 $out->addReturnTo( $this->getTitle() ); 00322 wfRunHooks( 'AddNewAccount', array( $u, false ) ); 00323 $u->addNewUserLogEntry( 'create2', $this->mReason ); 00324 } 00325 return true; 00326 } 00327 00334 public function addNewAccountInternal() { 00335 global $wgAuth, $wgMemc, $wgAccountCreationThrottle, 00336 $wgMinimalPasswordLength, $wgEmailConfirmToEdit; 00337 00338 // If the user passes an invalid domain, something is fishy 00339 if ( !$wgAuth->validDomain( $this->mDomain ) ) { 00340 return Status::newFatal( 'wrongpassword' ); 00341 } 00342 00343 // If we are not allowing users to login locally, we should be checking 00344 // to see if the user is actually able to authenticate to the authenti- 00345 // cation server before they create an account (otherwise, they can 00346 // create a local account and login as any domain user). We only need 00347 // to check this for domains that aren't local. 00348 if ( 'local' != $this->mDomain && $this->mDomain != '' ) { 00349 if ( 00350 !$wgAuth->canCreateAccounts() && 00351 ( 00352 !$wgAuth->userExists( $this->mUsername ) || 00353 !$wgAuth->authenticate( $this->mUsername, $this->mPassword ) 00354 ) 00355 ) { 00356 return Status::newFatal( 'wrongpassword' ); 00357 } 00358 } 00359 00360 if ( wfReadOnly() ) { 00361 throw new ReadOnlyError; 00362 } 00363 00364 # Request forgery checks. 00365 if ( !self::getCreateaccountToken() ) { 00366 self::setCreateaccountToken(); 00367 return Status::newFatal( 'nocookiesfornew' ); 00368 } 00369 00370 # The user didn't pass a createaccount token 00371 if ( !$this->mToken ) { 00372 return Status::newFatal( 'sessionfailure' ); 00373 } 00374 00375 # Validate the createaccount token 00376 if ( $this->mToken !== self::getCreateaccountToken() ) { 00377 return Status::newFatal( 'sessionfailure' ); 00378 } 00379 00380 # Check permissions 00381 $currentUser = $this->getUser(); 00382 $creationBlock = $currentUser->isBlockedFromCreateAccount(); 00383 if ( !$currentUser->isAllowed( 'createaccount' ) ) { 00384 throw new PermissionsError( 'createaccount' ); 00385 } elseif ( $creationBlock instanceof Block ) { 00386 // Throws an ErrorPageError. 00387 $this->userBlockedMessage( $creationBlock ); 00388 // This should never be reached. 00389 return false; 00390 } 00391 00392 # Include checks that will include GlobalBlocking (Bug 38333) 00393 $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $currentUser, true ); 00394 if ( count( $permErrors ) ) { 00395 throw new PermissionsError( 'createaccount', $permErrors ); 00396 } 00397 00398 $ip = $this->getRequest()->getIP(); 00399 if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) { 00400 return Status::newFatal( 'sorbs_create_account_reason' ); 00401 } 00402 00403 # Now create a dummy user ($u) and check if it is valid 00404 $name = trim( $this->mUsername ); 00405 $u = User::newFromName( $name, 'creatable' ); 00406 if ( !is_object( $u ) ) { 00407 return Status::newFatal( 'noname' ); 00408 } elseif ( 0 != $u->idForName() ) { 00409 return Status::newFatal( 'userexists' ); 00410 } 00411 00412 if ( $this->mCreateaccountMail ) { 00413 # do not force a password for account creation by email 00414 # set invalid password, it will be replaced later by a random generated password 00415 $this->mPassword = null; 00416 } else { 00417 if ( $this->mPassword !== $this->mRetype ) { 00418 return Status::newFatal( 'badretype' ); 00419 } 00420 00421 # check for minimal password length 00422 $valid = $u->getPasswordValidity( $this->mPassword ); 00423 if ( $valid !== true ) { 00424 if ( !is_array( $valid ) ) { 00425 $valid = array( $valid, $wgMinimalPasswordLength ); 00426 } 00427 return call_user_func_array( 'Status::newFatal', $valid ); 00428 } 00429 } 00430 00431 # if you need a confirmed email address to edit, then obviously you 00432 # need an email address. 00433 if ( $wgEmailConfirmToEdit && strval( $this->mEmail ) === '' ) { 00434 return Status::newFatal( 'noemailtitle' ); 00435 } 00436 00437 if ( strval( $this->mEmail ) !== '' && !Sanitizer::validateEmail( $this->mEmail ) ) { 00438 return Status::newFatal( 'invalidemailaddress' ); 00439 } 00440 00441 # Set some additional data so the AbortNewAccount hook can be used for 00442 # more than just username validation 00443 $u->setEmail( $this->mEmail ); 00444 $u->setRealName( $this->mRealName ); 00445 00446 $abortError = ''; 00447 if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) { 00448 // Hook point to add extra creation throttles and blocks 00449 wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" ); 00450 $abortError = new RawMessage( $abortError ); 00451 $abortError->text(); 00452 return Status::newFatal( $abortError ); 00453 } 00454 00455 // Hook point to check for exempt from account creation throttle 00456 if ( !wfRunHooks( 'ExemptFromAccountCreationThrottle', array( $ip ) ) ) { 00457 wfDebug( "LoginForm::exemptFromAccountCreationThrottle: a hook allowed account creation w/o throttle\n" ); 00458 } else { 00459 if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) { 00460 $key = wfMemcKey( 'acctcreate', 'ip', $ip ); 00461 $value = $wgMemc->get( $key ); 00462 if ( !$value ) { 00463 $wgMemc->set( $key, 0, 86400 ); 00464 } 00465 if ( $value >= $wgAccountCreationThrottle ) { 00466 return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle ); 00467 } 00468 $wgMemc->incr( $key ); 00469 } 00470 } 00471 00472 if ( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) { 00473 return Status::newFatal( 'externaldberror' ); 00474 } 00475 00476 self::clearCreateaccountToken(); 00477 return $this->initUser( $u, false ); 00478 } 00479 00489 function initUser( $u, $autocreate ) { 00490 global $wgAuth; 00491 00492 $status = $u->addToDatabase(); 00493 if ( !$status->isOK() ) { 00494 return $status; 00495 } 00496 00497 if ( $wgAuth->allowPasswordChange() ) { 00498 $u->setPassword( $this->mPassword ); 00499 } 00500 00501 $u->setEmail( $this->mEmail ); 00502 $u->setRealName( $this->mRealName ); 00503 $u->setToken(); 00504 00505 $wgAuth->initUser( $u, $autocreate ); 00506 00507 $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 ); 00508 $u->saveSettings(); 00509 00510 # Update user count 00511 DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) ); 00512 00513 return Status::newGood( $u ); 00514 } 00515 00524 public function authenticateUserData() { 00525 global $wgUser, $wgAuth; 00526 00527 $this->load(); 00528 00529 if ( $this->mUsername == '' ) { 00530 return self::NO_NAME; 00531 } 00532 00533 // We require a login token to prevent login CSRF 00534 // Handle part of this before incrementing the throttle so 00535 // token-less login attempts don't count towards the throttle 00536 // but wrong-token attempts do. 00537 00538 // If the user doesn't have a login token yet, set one. 00539 if ( !self::getLoginToken() ) { 00540 self::setLoginToken(); 00541 return self::NEED_TOKEN; 00542 } 00543 // If the user didn't pass a login token, tell them we need one 00544 if ( !$this->mToken ) { 00545 return self::NEED_TOKEN; 00546 } 00547 00548 $throttleCount = self::incLoginThrottle( $this->mUsername ); 00549 if ( $throttleCount === true ) { 00550 return self::THROTTLED; 00551 } 00552 00553 // Validate the login token 00554 if ( $this->mToken !== self::getLoginToken() ) { 00555 return self::WRONG_TOKEN; 00556 } 00557 00558 // Load the current user now, and check to see if we're logging in as 00559 // the same name. This is necessary because loading the current user 00560 // (say by calling getName()) calls the UserLoadFromSession hook, which 00561 // potentially creates the user in the database. Until we load $wgUser, 00562 // checking for user existence using User::newFromName($name)->getId() below 00563 // will effectively be using stale data. 00564 if ( $this->getUser()->getName() === $this->mUsername ) { 00565 wfDebug( __METHOD__ . ": already logged in as {$this->mUsername}\n" ); 00566 return self::SUCCESS; 00567 } 00568 00569 $u = User::newFromName( $this->mUsername ); 00570 if ( !( $u instanceof User ) || !User::isUsableName( $u->getName() ) ) { 00571 return self::ILLEGAL; 00572 } 00573 00574 $isAutoCreated = false; 00575 if ( $u->getID() == 0 ) { 00576 $status = $this->attemptAutoCreate( $u ); 00577 if ( $status !== self::SUCCESS ) { 00578 return $status; 00579 } else { 00580 $isAutoCreated = true; 00581 } 00582 } else { 00583 $u->load(); 00584 } 00585 00586 // Give general extensions, such as a captcha, a chance to abort logins 00587 $abort = self::ABORTED; 00588 $msg = null; 00589 if ( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) { 00590 $this->mAbortLoginErrorMsg = $msg; 00591 return $abort; 00592 } 00593 00594 global $wgBlockDisablesLogin; 00595 if ( !$u->checkPassword( $this->mPassword ) ) { 00596 if ( $u->checkTemporaryPassword( $this->mPassword ) ) { 00597 // The e-mailed temporary password should not be used for actu- 00598 // al logins; that's a very sloppy habit, and insecure if an 00599 // attacker has a few seconds to click "search" on someone's o- 00600 // pen mail reader. 00601 // 00602 // Allow it to be used only to reset the password a single time 00603 // to a new value, which won't be in the user's e-mail ar- 00604 // chives. 00605 // 00606 // For backwards compatibility, we'll still recognize it at the 00607 // login form to minimize surprises for people who have been 00608 // logging in with a temporary password for some time. 00609 // 00610 // As a side-effect, we can authenticate the user's e-mail ad- 00611 // dress if it's not already done, since the temporary password 00612 // was sent via e-mail. 00613 if ( !$u->isEmailConfirmed() ) { 00614 $u->confirmEmail(); 00615 $u->saveSettings(); 00616 } 00617 00618 // At this point we just return an appropriate code/ indicating 00619 // that the UI should show a password reset form; bot inter- 00620 // faces etc will probably just fail cleanly here. 00621 $retval = self::RESET_PASS; 00622 } else { 00623 $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS; 00624 } 00625 } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) { 00626 // If we've enabled it, make it so that a blocked user cannot login 00627 $retval = self::USER_BLOCKED; 00628 } else { 00629 $wgAuth->updateUser( $u ); 00630 $wgUser = $u; 00631 // This should set it for OutputPage and the Skin 00632 // which is needed or the personal links will be 00633 // wrong. 00634 $this->getContext()->setUser( $u ); 00635 00636 // Please reset throttle for successful logins, thanks! 00637 if ( $throttleCount ) { 00638 self::clearLoginThrottle( $this->mUsername ); 00639 } 00640 00641 if ( $isAutoCreated ) { 00642 // Must be run after $wgUser is set, for correct new user log 00643 wfRunHooks( 'AuthPluginAutoCreate', array( $u ) ); 00644 } 00645 00646 $retval = self::SUCCESS; 00647 } 00648 wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) ); 00649 return $retval; 00650 } 00651 00658 public static function incLoginThrottle( $username ) { 00659 global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest; 00660 $username = trim( $username ); // sanity 00661 00662 $throttleCount = 0; 00663 if ( is_array( $wgPasswordAttemptThrottle ) ) { 00664 $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) ); 00665 $count = $wgPasswordAttemptThrottle['count']; 00666 $period = $wgPasswordAttemptThrottle['seconds']; 00667 00668 $throttleCount = $wgMemc->get( $throttleKey ); 00669 if ( !$throttleCount ) { 00670 $wgMemc->add( $throttleKey, 1, $period ); // start counter 00671 } elseif ( $throttleCount < $count ) { 00672 $wgMemc->incr( $throttleKey ); 00673 } elseif ( $throttleCount >= $count ) { 00674 return true; 00675 } 00676 } 00677 00678 return $throttleCount; 00679 } 00680 00686 public static function clearLoginThrottle( $username ) { 00687 global $wgMemc, $wgRequest; 00688 $username = trim( $username ); // sanity 00689 00690 $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) ); 00691 $wgMemc->delete( $throttleKey ); 00692 } 00693 00702 function attemptAutoCreate( $user ) { 00703 global $wgAuth; 00704 00705 if ( $this->getUser()->isBlockedFromCreateAccount() ) { 00706 wfDebug( __METHOD__ . ": user is blocked from account creation\n" ); 00707 return self::CREATE_BLOCKED; 00708 } 00709 if ( !$wgAuth->autoCreate() ) { 00710 return self::NOT_EXISTS; 00711 } 00712 if ( !$wgAuth->userExists( $user->getName() ) ) { 00713 wfDebug( __METHOD__ . ": user does not exist\n" ); 00714 return self::NOT_EXISTS; 00715 } 00716 if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) { 00717 wfDebug( __METHOD__ . ": \$wgAuth->authenticate() returned false, aborting\n" ); 00718 return self::WRONG_PLUGIN_PASS; 00719 } 00720 00721 $abortError = ''; 00722 if ( !wfRunHooks( 'AbortAutoAccount', array( $user, &$abortError ) ) ) { 00723 // Hook point to add extra creation throttles and blocks 00724 wfDebug( "LoginForm::attemptAutoCreate: a hook blocked creation: $abortError\n" ); 00725 $this->mAbortLoginErrorMsg = $abortError; 00726 return self::ABORTED; 00727 } 00728 00729 wfDebug( __METHOD__ . ": creating account\n" ); 00730 $status = $this->initUser( $user, true ); 00731 00732 if ( !$status->isOK() ) { 00733 $errors = $status->getErrorsByType( 'error' ); 00734 $this->mAbortLoginErrorMsg = $errors[0]['message']; 00735 return self::ABORTED; 00736 } 00737 00738 return self::SUCCESS; 00739 } 00740 00741 function processLogin() { 00742 global $wgMemc, $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle; 00743 00744 switch ( $this->authenticateUserData() ) { 00745 case self::SUCCESS: 00746 # We've verified now, update the real record 00747 $user = $this->getUser(); 00748 if ( (bool)$this->mRemember != $user->getBoolOption( 'rememberpassword' ) ) { 00749 $user->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 ); 00750 $user->saveSettings(); 00751 } else { 00752 $user->invalidateCache(); 00753 } 00754 00755 if ( $user->requiresHTTPS() ) { 00756 $this->mStickHTTPS = true; 00757 } 00758 00759 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 00760 $user->setCookies( null, false ); 00761 } else { 00762 $user->setCookies(); 00763 } 00764 self::clearLoginToken(); 00765 00766 // Reset the throttle 00767 $request = $this->getRequest(); 00768 $key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) ); 00769 $wgMemc->delete( $key ); 00770 00771 if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) { 00772 /* Replace the language object to provide user interface in 00773 * correct language immediately on this first page load. 00774 */ 00775 $code = $request->getVal( 'uselang', $user->getOption( 'language' ) ); 00776 $userLang = Language::factory( $code ); 00777 $wgLang = $userLang; 00778 $this->getContext()->setLanguage( $userLang ); 00779 // Reset SessionID on Successful login (bug 40995) 00780 $this->renewSessionId(); 00781 $this->successfulLogin(); 00782 } else { 00783 $this->cookieRedirectCheck( 'login' ); 00784 } 00785 break; 00786 00787 case self::NEED_TOKEN: 00788 $error = $this->mAbortLoginErrorMsg ?: 'nocookiesforlogin'; 00789 $this->mainLoginForm( $this->msg( $error )->parse() ); 00790 break; 00791 case self::WRONG_TOKEN: 00792 $error = $this->mAbortLoginErrorMsg ?: 'sessionfailure'; 00793 $this->mainLoginForm( $this->msg( $error )->text() ); 00794 break; 00795 case self::NO_NAME: 00796 case self::ILLEGAL: 00797 $error = $this->mAbortLoginErrorMsg ?: 'noname'; 00798 $this->mainLoginForm( $this->msg( $error )->text() ); 00799 break; 00800 case self::WRONG_PLUGIN_PASS: 00801 $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword'; 00802 $this->mainLoginForm( $this->msg( $error )->text() ); 00803 break; 00804 case self::NOT_EXISTS: 00805 if ( $this->getUser()->isAllowed( 'createaccount' ) ) { 00806 $error = $this->mAbortLoginErrorMsg ?: 'nosuchuser'; 00807 $this->mainLoginForm( $this->msg( $error, 00808 wfEscapeWikiText( $this->mUsername ) )->parse() ); 00809 } else { 00810 $error = $this->mAbortLoginErrorMsg ?: 'nosuchusershort'; 00811 $this->mainLoginForm( $this->msg( $error, 00812 wfEscapeWikiText( $this->mUsername ) )->text() ); 00813 } 00814 break; 00815 case self::WRONG_PASS: 00816 $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword'; 00817 $this->mainLoginForm( $this->msg( $error )->text() ); 00818 break; 00819 case self::EMPTY_PASS: 00820 $error = $this->mAbortLoginErrorMsg ?: 'wrongpasswordempty'; 00821 $this->mainLoginForm( $this->msg( $error )->text() ); 00822 break; 00823 case self::RESET_PASS: 00824 $error = $this->mAbortLoginErrorMsg ?: 'resetpass_announce'; 00825 $this->resetLoginForm( $this->msg( $error )->text() ); 00826 break; 00827 case self::CREATE_BLOCKED: 00828 $this->userBlockedMessage( $this->getUser()->isBlockedFromCreateAccount() ); 00829 break; 00830 case self::THROTTLED: 00831 $error = $this->mAbortLoginErrorMsg ?: 'login-throttled'; 00832 $this->mainLoginForm( $this->msg( $error ) 00833 ->params ( $this->getLanguage()->formatDuration( $wgPasswordAttemptThrottle['seconds'] ) ) 00834 ->text() 00835 ); 00836 break; 00837 case self::USER_BLOCKED: 00838 $error = $this->mAbortLoginErrorMsg ?: 'login-userblocked'; 00839 $this->mainLoginForm( $this->msg( $error, $this->mUsername )->escaped() ); 00840 break; 00841 case self::ABORTED: 00842 $error = $this->mAbortLoginErrorMsg ?: 'login-abort-generic'; 00843 $this->mainLoginForm( $this->msg( $error )->text() ); 00844 break; 00845 default: 00846 throw new MWException( 'Unhandled case value' ); 00847 } 00848 } 00849 00853 function resetLoginForm( $error ) { 00854 $this->getOutput()->addHTML( Xml::element( 'p', array( 'class' => 'error' ), $error ) ); 00855 $reset = new SpecialChangePassword(); 00856 $reset->setContext( $this->getContext() ); 00857 $reset->execute( null ); 00858 } 00859 00867 function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) { 00868 global $wgCanonicalServer, $wgScript, $wgNewPasswordExpiry; 00869 00870 if ( $u->getEmail() == '' ) { 00871 return Status::newFatal( 'noemail', $u->getName() ); 00872 } 00873 $ip = $this->getRequest()->getIP(); 00874 if ( !$ip ) { 00875 return Status::newFatal( 'badipaddress' ); 00876 } 00877 00878 $currentUser = $this->getUser(); 00879 wfRunHooks( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) ); 00880 00881 $np = $u->randomPassword(); 00882 $u->setNewpassword( $np, $throttle ); 00883 $u->saveSettings(); 00884 $userLanguage = $u->getOption( 'language' ); 00885 $m = $this->msg( $emailText, $ip, $u->getName(), $np, '<' . $wgCanonicalServer . $wgScript . '>', 00886 round( $wgNewPasswordExpiry / 86400 ) )->inLanguage( $userLanguage )->text(); 00887 $result = $u->sendMail( $this->msg( $emailTitle )->inLanguage( $userLanguage )->text(), $m ); 00888 00889 return $result; 00890 } 00891 00902 function successfulLogin() { 00903 # Run any hooks; display injected HTML if any, else redirect 00904 $currentUser = $this->getUser(); 00905 $injected_html = ''; 00906 wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); 00907 00908 if ( $injected_html !== '' ) { 00909 $this->displaySuccessfulAction( $this->msg( 'loginsuccesstitle' ), 00910 'loginsuccess', $injected_html ); 00911 } else { 00912 $this->executeReturnTo( 'successredirect' ); 00913 } 00914 } 00915 00922 function successfulCreation() { 00923 # Run any hooks; display injected HTML 00924 $currentUser = $this->getUser(); 00925 $injected_html = ''; 00926 $welcome_creation_msg = 'welcomecreation-msg'; 00927 00928 wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); 00929 00935 wfRunHooks( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) ); 00936 00937 $this->displaySuccessfulAction( $this->msg( 'welcomeuser', $this->getUser()->getName() ), 00938 $welcome_creation_msg, $injected_html ); 00939 } 00940 00948 private function displaySuccessfulAction( $title, $msgname, $injected_html ) { 00949 $out = $this->getOutput(); 00950 $out->setPageTitle( $title ); 00951 if ( $msgname ) { 00952 $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) ); 00953 } 00954 00955 $out->addHTML( $injected_html ); 00956 00957 $this->executeReturnTo( 'success' ); 00958 } 00959 00968 function userBlockedMessage( Block $block ) { 00969 # Let's be nice about this, it's likely that this feature will be used 00970 # for blocking large numbers of innocent people, e.g. range blocks on 00971 # schools. Don't blame it on the user. There's a small chance that it 00972 # really is the user's fault, i.e. the username is blocked and they 00973 # haven't bothered to log out before trying to create an account to 00974 # evade it, but we'll leave that to their guilty conscience to figure 00975 # out. 00976 throw new ErrorPageError( 00977 'cantcreateaccounttitle', 00978 'cantcreateaccount-text', 00979 array( 00980 $block->getTarget(), 00981 $block->mReason ? $block->mReason : $this->msg( 'blockednoreason' )->text(), 00982 $block->getByName() 00983 ) 00984 ); 00985 } 00986 01001 public function showReturnToPage( 01002 $type, $returnTo = '', $returnToQuery = '', $stickHTTPs = false 01003 ) { 01004 $this->mReturnTo = $returnTo; 01005 $this->mReturnToQuery = $returnToQuery; 01006 $this->mStickHTTPS = $stickHTTPs; 01007 $this->executeReturnTo( $type ); 01008 } 01009 01018 private function executeReturnTo( $type ) { 01019 global $wgRedirectOnLogin, $wgSecureLogin; 01020 01021 if ( $type != 'error' && $wgRedirectOnLogin !== null ) { 01022 $returnTo = $wgRedirectOnLogin; 01023 $returnToQuery = array(); 01024 } else { 01025 $returnTo = $this->mReturnTo; 01026 $returnToQuery = wfCgiToArray( $this->mReturnToQuery ); 01027 } 01028 01029 $returnToTitle = Title::newFromText( $returnTo ); 01030 if ( !$returnToTitle ) { 01031 $returnToTitle = Title::newMainPage(); 01032 } 01033 01034 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 01035 $options = array( 'http' ); 01036 $proto = PROTO_HTTP; 01037 } elseif ( $wgSecureLogin ) { 01038 $options = array( 'https' ); 01039 $proto = PROTO_HTTPS; 01040 } else { 01041 $options = array(); 01042 $proto = PROTO_RELATIVE; 01043 } 01044 01045 if ( $type == 'successredirect' ) { 01046 $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto ); 01047 $this->getOutput()->redirect( $redirectUrl ); 01048 } else { 01049 $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options ); 01050 } 01051 } 01052 01056 function mainLoginForm( $msg, $msgtype = 'error' ) { 01057 global $wgEnableEmail, $wgEnableUserEmail; 01058 global $wgHiddenPrefs, $wgLoginLanguageSelector; 01059 global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration; 01060 global $wgSecureLogin, $wgPasswordResetRoutes; 01061 01062 $titleObj = $this->getTitle(); 01063 $user = $this->getUser(); 01064 $out = $this->getOutput(); 01065 01066 if ( $this->mType == 'signup' ) { 01067 // Block signup here if in readonly. Keeps user from 01068 // going through the process (filling out data, etc) 01069 // and being informed later. 01070 $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $user, true ); 01071 if ( count( $permErrors ) ) { 01072 throw new PermissionsError( 'createaccount', $permErrors ); 01073 } elseif ( $user->isBlockedFromCreateAccount() ) { 01074 $this->userBlockedMessage( $user->isBlockedFromCreateAccount() ); 01075 return; 01076 } elseif ( wfReadOnly() ) { 01077 throw new ReadOnlyError; 01078 } 01079 } 01080 01081 // Pre-fill username (if not creating an account, bug 44775). 01082 if ( $this->mUsername == '' && $this->mType != 'signup' ) { 01083 if ( $user->isLoggedIn() ) { 01084 $this->mUsername = $user->getName(); 01085 } else { 01086 $this->mUsername = $this->getRequest()->getCookie( 'UserName' ); 01087 } 01088 } 01089 01090 if ( $this->mType == 'signup' ) { 01091 $template = new UsercreateTemplate(); 01092 01093 $out->addModuleStyles( array( 01094 'mediawiki.ui', 01095 'mediawiki.special.createaccount' 01096 ) ); 01097 // XXX hack pending RL or JS parse() support for complex content messages 01098 // https://bugzilla.wikimedia.org/show_bug.cgi?id=25349 01099 $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp', 01100 $this->msg( 'createacct-imgcaptcha-help' )->parse() ); 01101 $out->addModules( array( 01102 'mediawiki.special.createaccount.js' 01103 ) ); 01104 // Must match number of benefits defined in messages 01105 $template->set( 'benefitCount', 3 ); 01106 01107 $q = 'action=submitlogin&type=signup'; 01108 $linkq = 'type=login'; 01109 } else { 01110 $template = new UserloginTemplate(); 01111 01112 $out->addModuleStyles( array( 01113 'mediawiki.ui', 01114 'mediawiki.special.userlogin' 01115 ) ); 01116 01117 $q = 'action=submitlogin&type=login'; 01118 $linkq = 'type=signup'; 01119 } 01120 01121 if ( $this->mReturnTo !== '' ) { 01122 $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo ); 01123 if ( $this->mReturnToQuery !== '' ) { 01124 $returnto .= '&returntoquery=' . 01125 wfUrlencode( $this->mReturnToQuery ); 01126 } 01127 $q .= $returnto; 01128 $linkq .= $returnto; 01129 } 01130 01131 # Don't show a "create account" link if the user can't. 01132 if ( $this->showCreateOrLoginLink( $user ) ) { 01133 # Pass any language selection on to the mode switch link 01134 if ( $wgLoginLanguageSelector && $this->mLanguage ) { 01135 $linkq .= '&uselang=' . $this->mLanguage; 01136 } 01137 // Supply URL, login template creates the button. 01138 $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) ); 01139 } else { 01140 $template->set( 'link', '' ); 01141 } 01142 01143 $resetLink = $this->mType == 'signup' 01144 ? null 01145 : is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) ); 01146 01147 $template->set( 'header', '' ); 01148 $template->set( 'skin', $this->getSkin() ); 01149 $template->set( 'name', $this->mUsername ); 01150 $template->set( 'password', $this->mPassword ); 01151 $template->set( 'retype', $this->mRetype ); 01152 $template->set( 'createemailset', $this->mCreateaccountMail ); 01153 $template->set( 'email', $this->mEmail ); 01154 $template->set( 'realname', $this->mRealName ); 01155 $template->set( 'domain', $this->mDomain ); 01156 $template->set( 'reason', $this->mReason ); 01157 01158 $template->set( 'action', $titleObj->getLocalURL( $q ) ); 01159 $template->set( 'message', $msg ); 01160 $template->set( 'messagetype', $msgtype ); 01161 $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() ); 01162 $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) ); 01163 $template->set( 'useemail', $wgEnableEmail ); 01164 $template->set( 'emailrequired', $wgEmailConfirmToEdit ); 01165 $template->set( 'emailothers', $wgEnableUserEmail ); 01166 $template->set( 'canreset', $wgAuth->allowPasswordChange() ); 01167 $template->set( 'resetlink', $resetLink ); 01168 $template->set( 'canremember', ( $wgCookieExpiration > 0 ) ); 01169 $template->set( 'usereason', $user->isLoggedIn() ); 01170 $template->set( 'remember', $user->getOption( 'rememberpassword' ) || $this->mRemember ); 01171 $template->set( 'cansecurelogin', ( $wgSecureLogin === true ) ); 01172 $template->set( 'stickhttps', (int)$this->mStickHTTPS ); 01173 $template->set( 'loggedin', $user->isLoggedIn() ); 01174 $template->set( 'loggedinuser', $user->getName() ); 01175 01176 if ( $this->mType == 'signup' ) { 01177 if ( !self::getCreateaccountToken() ) { 01178 self::setCreateaccountToken(); 01179 } 01180 $template->set( 'token', self::getCreateaccountToken() ); 01181 } else { 01182 if ( !self::getLoginToken() ) { 01183 self::setLoginToken(); 01184 } 01185 $template->set( 'token', self::getLoginToken() ); 01186 } 01187 01188 # Prepare language selection links as needed 01189 if ( $wgLoginLanguageSelector ) { 01190 $template->set( 'languages', $this->makeLanguageSelector() ); 01191 if ( $this->mLanguage ) { 01192 $template->set( 'uselang', $this->mLanguage ); 01193 } 01194 } 01195 01196 $template->set( 'secureLoginUrl', $this->mSecureLoginUrl ); 01197 // Use loginend-https for HTTPS requests if it's not blank, loginend otherwise 01198 // Ditto for signupend. New forms use neither. 01199 $usingHTTPS = WebRequest::detectProtocol() == 'https'; 01200 $loginendHTTPS = $this->msg( 'loginend-https' ); 01201 $signupendHTTPS = $this->msg( 'signupend-https' ); 01202 if ( $usingHTTPS && !$loginendHTTPS->isBlank() ) { 01203 $template->set( 'loginend', $loginendHTTPS->parse() ); 01204 } else { 01205 $template->set( 'loginend', $this->msg( 'loginend' )->parse() ); 01206 } 01207 if ( $usingHTTPS && !$signupendHTTPS->isBlank() ) { 01208 $template->set( 'signupend', $signupendHTTPS->parse() ); 01209 } else { 01210 $template->set( 'signupend', $this->msg( 'signupend' )->parse() ); 01211 } 01212 01213 // Give authentication and captcha plugins a chance to modify the form 01214 $wgAuth->modifyUITemplate( $template, $this->mType ); 01215 if ( $this->mType == 'signup' ) { 01216 wfRunHooks( 'UserCreateForm', array( &$template ) ); 01217 } else { 01218 wfRunHooks( 'UserLoginForm', array( &$template ) ); 01219 } 01220 01221 $out->disallowUserJs(); // just in case... 01222 $out->addTemplate( $template ); 01223 } 01224 01232 private function showCreateOrLoginLink( &$user ) { 01233 if ( $this->mType == 'signup' ) { 01234 return true; 01235 } elseif ( $user->isAllowed( 'createaccount' ) ) { 01236 return true; 01237 } else { 01238 return false; 01239 } 01240 } 01241 01252 function hasSessionCookie() { 01253 global $wgDisableCookieCheck; 01254 return $wgDisableCookieCheck ? true : $this->getRequest()->checkSessionCookie(); 01255 } 01256 01261 public static function getLoginToken() { 01262 global $wgRequest; 01263 return $wgRequest->getSessionData( 'wsLoginToken' ); 01264 } 01265 01269 public static function setLoginToken() { 01270 global $wgRequest; 01271 // Generate a token directly instead of using $user->editToken() 01272 // because the latter reuses $_SESSION['wsEditToken'] 01273 $wgRequest->setSessionData( 'wsLoginToken', MWCryptRand::generateHex( 32 ) ); 01274 } 01275 01279 public static function clearLoginToken() { 01280 global $wgRequest; 01281 $wgRequest->setSessionData( 'wsLoginToken', null ); 01282 } 01283 01288 public static function getCreateaccountToken() { 01289 global $wgRequest; 01290 return $wgRequest->getSessionData( 'wsCreateaccountToken' ); 01291 } 01292 01296 public static function setCreateaccountToken() { 01297 global $wgRequest; 01298 $wgRequest->setSessionData( 'wsCreateaccountToken', MWCryptRand::generateHex( 32 ) ); 01299 } 01300 01304 public static function clearCreateaccountToken() { 01305 global $wgRequest; 01306 $wgRequest->setSessionData( 'wsCreateaccountToken', null ); 01307 } 01308 01312 private function renewSessionId() { 01313 global $wgSecureLogin, $wgCookieSecure; 01314 if ( $wgSecureLogin && !$this->mStickHTTPS ) { 01315 $wgCookieSecure = false; 01316 } 01317 01318 wfResetSessionID(); 01319 } 01320 01324 function cookieRedirectCheck( $type ) { 01325 $titleObj = SpecialPage::getTitleFor( 'Userlogin' ); 01326 $query = array( 'wpCookieCheck' => $type ); 01327 if ( $this->mReturnTo !== '' ) { 01328 $query['returnto'] = $this->mReturnTo; 01329 $query['returntoquery'] = $this->mReturnToQuery; 01330 } 01331 $check = $titleObj->getFullURL( $query ); 01332 01333 $this->getOutput()->redirect( $check ); 01334 } 01335 01339 function onCookieRedirectCheck( $type ) { 01340 if ( !$this->hasSessionCookie() ) { 01341 if ( $type == 'new' ) { 01342 $this->mainLoginForm( $this->msg( 'nocookiesnew' )->parse() ); 01343 } elseif ( $type == 'login' ) { 01344 $this->mainLoginForm( $this->msg( 'nocookieslogin' )->parse() ); 01345 } else { 01346 # shouldn't happen 01347 $this->mainLoginForm( $this->msg( 'error' )->text() ); 01348 } 01349 } else { 01350 $this->successfulLogin(); 01351 } 01352 } 01353 01360 function makeLanguageSelector() { 01361 $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage(); 01362 if ( !$msg->isBlank() ) { 01363 $langs = explode( "\n", $msg->text() ); 01364 $links = array(); 01365 foreach ( $langs as $lang ) { 01366 $lang = trim( $lang, '* ' ); 01367 $parts = explode( '|', $lang ); 01368 if ( count( $parts ) >= 2 ) { 01369 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) ); 01370 } 01371 } 01372 return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams( 01373 $this->getLanguage()->pipeList( $links ) )->escaped() : ''; 01374 } else { 01375 return ''; 01376 } 01377 } 01378 01387 function makeLanguageSelectorLink( $text, $lang ) { 01388 if ( $this->getLanguage()->getCode() == $lang ) { 01389 // no link for currently used language 01390 return htmlspecialchars( $text ); 01391 } 01392 $query = array( 'uselang' => $lang ); 01393 if ( $this->mType == 'signup' ) { 01394 $query['type'] = 'signup'; 01395 } 01396 if ( $this->mReturnTo !== '' ) { 01397 $query['returnto'] = $this->mReturnTo; 01398 $query['returntoquery'] = $this->mReturnToQuery; 01399 } 01400 01401 $attr = array(); 01402 $targetLanguage = Language::factory( $lang ); 01403 $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode(); 01404 01405 return Linker::linkKnown( 01406 $this->getTitle(), 01407 htmlspecialchars( $text ), 01408 $attr, 01409 $query 01410 ); 01411 } 01412 01413 protected function getGroupName() { 01414 return 'login'; 01415 } 01416 }