[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/specials/ -> SpecialUserlogin.php (source)

   1  <?php
   2  /**
   3   * Implements Special:UserLogin
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup SpecialPage
  22   */
  23  
  24  /**
  25   * Implements Special:UserLogin
  26   *
  27   * @ingroup SpecialPage
  28   */
  29  class LoginForm extends SpecialPage {
  30      const SUCCESS = 0;
  31      const NO_NAME = 1;
  32      const ILLEGAL = 2;
  33      const WRONG_PLUGIN_PASS = 3;
  34      const NOT_EXISTS = 4;
  35      const WRONG_PASS = 5;
  36      const EMPTY_PASS = 6;
  37      const RESET_PASS = 7;
  38      const ABORTED = 8;
  39      const CREATE_BLOCKED = 9;
  40      const THROTTLED = 10;
  41      const USER_BLOCKED = 11;
  42      const NEED_TOKEN = 12;
  43      const WRONG_TOKEN = 13;
  44      const USER_MIGRATED = 14;
  45  
  46      /**
  47       * Valid error and warning messages
  48       *
  49       * Special:Userlogin can show an error or warning message on the form when
  50       * coming from another page. This is done via the ?error= or ?warning= GET
  51       * parameters.
  52       *
  53       * This array is the list of valid message keys. All other values will be
  54       * ignored.
  55       *
  56       * @since 1.24
  57       * @var string[]
  58       */
  59      public static $validErrorMessages = array(
  60          'exception-nologin-text',
  61          'watchlistanontext',
  62          'changeemail-no-info',
  63          'resetpass-no-info',
  64          'confirmemail_needlogin',
  65          'prefsnologintext2',
  66      );
  67  
  68      public $mAbortLoginErrorMsg = null;
  69  
  70      protected $mUsername;
  71      protected $mPassword;
  72      protected $mRetype;
  73      protected $mReturnTo;
  74      protected $mCookieCheck;
  75      protected $mPosted;
  76      protected $mAction;
  77      protected $mCreateaccount;
  78      protected $mCreateaccountMail;
  79      protected $mLoginattempt;
  80      protected $mRemember;
  81      protected $mEmail;
  82      protected $mDomain;
  83      protected $mLanguage;
  84      protected $mSkipCookieCheck;
  85      protected $mReturnToQuery;
  86      protected $mToken;
  87      protected $mStickHTTPS;
  88      protected $mType;
  89      protected $mReason;
  90      protected $mRealName;
  91      protected $mEntryError = '';
  92      protected $mEntryErrorType = 'error';
  93  
  94      private $mTempPasswordUsed;
  95      private $mLoaded = false;
  96      private $mSecureLoginUrl;
  97  
  98      /** @var WebRequest */
  99      private $mOverrideRequest = null;
 100  
 101      /** @var WebRequest Effective request; set at the beginning of load */
 102      private $mRequest = null;
 103  
 104      /**
 105       * @param WebRequest $request
 106       */
 107  	public function __construct( $request = null ) {
 108          parent::__construct( 'Userlogin' );
 109  
 110          $this->mOverrideRequest = $request;
 111      }
 112  
 113      /**
 114       * Loader
 115       */
 116  	function load() {
 117          global $wgAuth, $wgHiddenPrefs, $wgEnableEmail;
 118  
 119          if ( $this->mLoaded ) {
 120              return;
 121          }
 122          $this->mLoaded = true;
 123  
 124          if ( $this->mOverrideRequest === null ) {
 125              $request = $this->getRequest();
 126          } else {
 127              $request = $this->mOverrideRequest;
 128          }
 129          $this->mRequest = $request;
 130  
 131          $this->mType = $request->getText( 'type' );
 132          $this->mUsername = $request->getText( 'wpName' );
 133          $this->mPassword = $request->getText( 'wpPassword' );
 134          $this->mRetype = $request->getText( 'wpRetype' );
 135          $this->mDomain = $request->getText( 'wpDomain' );
 136          $this->mReason = $request->getText( 'wpReason' );
 137          $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
 138          $this->mPosted = $request->wasPosted();
 139          $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
 140              && $wgEnableEmail;
 141          $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ) && !$this->mCreateaccountMail;
 142          $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
 143          $this->mAction = $request->getVal( 'action' );
 144          $this->mRemember = $request->getCheck( 'wpRemember' );
 145          $this->mFromHTTP = $request->getBool( 'fromhttp', false );
 146          $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
 147              || $request->getBool( 'wpForceHttps', false );
 148          $this->mLanguage = $request->getText( 'uselang' );
 149          $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
 150          $this->mToken = $this->mType == 'signup'
 151              ? $request->getVal( 'wpCreateaccountToken' )
 152              : $request->getVal( 'wpLoginToken' );
 153          $this->mReturnTo = $request->getVal( 'returnto', '' );
 154          $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
 155  
 156          // Show an error or warning passed on from a previous page
 157          $entryError = $this->msg( $request->getVal( 'error', '' ) );
 158          $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
 159          // bc: provide login link as a parameter for messages where the translation
 160          // was not updated
 161          $loginreqlink = Linker::linkKnown(
 162              $this->getPageTitle(),
 163              $this->msg( 'loginreqlink' )->escaped(),
 164              array(),
 165              array(
 166                  'returnto' => $this->mReturnTo,
 167                  'returntoquery' => $this->mReturnToQuery,
 168                  'uselang' => $this->mLanguage,
 169                  'fromhttp' => $this->mFromHTTP ? '1' : '0',
 170              )
 171          );
 172  
 173          // Only show valid error or warning messages.
 174          if ( $entryError->exists()
 175              && in_array( $entryError->getKey(), self::$validErrorMessages )
 176          ) {
 177              $this->mEntryErrorType = 'error';
 178              $this->mEntryError = $entryError->rawParams( $loginreqlink )->escaped();
 179  
 180          } elseif ( $entryWarning->exists()
 181              && in_array( $entryWarning->getKey(), self::$validErrorMessages )
 182          ) {
 183              $this->mEntryErrorType = 'warning';
 184              $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->escaped();
 185          }
 186  
 187          if ( $wgEnableEmail ) {
 188              $this->mEmail = $request->getText( 'wpEmail' );
 189          } else {
 190              $this->mEmail = '';
 191          }
 192          if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
 193              $this->mRealName = $request->getText( 'wpRealName' );
 194          } else {
 195              $this->mRealName = '';
 196          }
 197  
 198          if ( !$wgAuth->validDomain( $this->mDomain ) ) {
 199              $this->mDomain = $wgAuth->getDomain();
 200          }
 201          $wgAuth->setDomain( $this->mDomain );
 202  
 203          # 1. When switching accounts, it sucks to get automatically logged out
 204          # 2. Do not return to PasswordReset after a successful password change
 205          #    but goto Wiki start page (Main_Page) instead ( bug 33997 )
 206          $returnToTitle = Title::newFromText( $this->mReturnTo );
 207          if ( is_object( $returnToTitle )
 208              && ( $returnToTitle->isSpecial( 'Userlogout' )
 209                  || $returnToTitle->isSpecial( 'PasswordReset' ) )
 210          ) {
 211              $this->mReturnTo = '';
 212              $this->mReturnToQuery = '';
 213          }
 214      }
 215  
 216  	function getDescription() {
 217          if ( $this->mType === 'signup' ) {
 218              return $this->msg( 'createaccount' )->text();
 219          } else {
 220              return $this->msg( 'login' )->text();
 221          }
 222      }
 223  
 224      /**
 225       * @param string|null $subPage
 226       */
 227  	public function execute( $subPage ) {
 228          if ( session_id() == '' ) {
 229              wfSetupSession();
 230          }
 231  
 232          $this->load();
 233  
 234          // Check for [[Special:Userlogin/signup]]. This affects form display and
 235          // page title.
 236          if ( $subPage == 'signup' ) {
 237              $this->mType = 'signup';
 238          }
 239          $this->setHeaders();
 240  
 241          // In the case where the user is already logged in, and was redirected to the login form from a
 242          // page that requires login, do not show the login page. The use case scenario for this is when
 243          // a user opens a large number of tabs, is redirected to the login page on all of them, and then
 244          // logs in on one, expecting all the others to work properly.
 245          //
 246          // However, do show the form if it was visited intentionally (no 'returnto' is present). People
 247          // who often switch between several accounts have grown accustomed to this behavior.
 248          if (
 249              $this->mType !== 'signup' &&
 250              !$this->mPosted &&
 251              $this->getUser()->isLoggedIn() &&
 252              ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' )
 253          ) {
 254              $this->successfulLogin();
 255          }
 256  
 257          // If logging in and not on HTTPS, either redirect to it or offer a link.
 258          global $wgSecureLogin;
 259          if ( $this->mRequest->getProtocol() !== 'https' ) {
 260              $title = $this->getFullTitle();
 261              $query = array(
 262                  'returnto' => $this->mReturnTo !== '' ? $this->mReturnTo : null,
 263                  'returntoquery' => $this->mReturnToQuery !== '' ?
 264                      $this->mReturnToQuery : null,
 265                  'title' => null,
 266                  ( $this->mEntryErrorType === 'error' ? 'error' : 'warning' ) => $this->mEntryError,
 267              ) + $this->mRequest->getQueryValues();
 268              $url = $title->getFullURL( $query, false, PROTO_HTTPS );
 269              if ( $wgSecureLogin
 270                  && wfCanIPUseHTTPS( $this->getRequest()->getIP() )
 271                  && !$this->mFromHTTP ) // Avoid infinite redirect
 272              {
 273                  $url = wfAppendQuery( $url, 'fromhttp=1' );
 274                  $this->getOutput()->redirect( $url );
 275                  // Since we only do this redir to change proto, always vary
 276                  $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
 277  
 278                  return;
 279              } else {
 280                  // A wiki without HTTPS login support should set $wgServer to
 281                  // http://somehost, in which case the secure URL generated
 282                  // above won't actually start with https://
 283                  if ( substr( $url, 0, 8 ) === 'https://' ) {
 284                      $this->mSecureLoginUrl = $url;
 285                  }
 286              }
 287          }
 288  
 289          if ( !is_null( $this->mCookieCheck ) ) {
 290              $this->onCookieRedirectCheck( $this->mCookieCheck );
 291  
 292              return;
 293          } elseif ( $this->mPosted ) {
 294              if ( $this->mCreateaccount ) {
 295                  $this->addNewAccount();
 296  
 297                  return;
 298              } elseif ( $this->mCreateaccountMail ) {
 299                  $this->addNewAccountMailPassword();
 300  
 301                  return;
 302              } elseif ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
 303                  $this->processLogin();
 304  
 305                  return;
 306              }
 307          }
 308          $this->mainLoginForm( $this->mEntryError, $this->mEntryErrorType );
 309      }
 310  
 311      /**
 312       * @private
 313       */
 314  	function addNewAccountMailPassword() {
 315          if ( $this->mEmail == '' ) {
 316              $this->mainLoginForm( $this->msg( 'noemailcreate' )->escaped() );
 317  
 318              return;
 319          }
 320  
 321          $status = $this->addNewAccountInternal();
 322          if ( !$status->isGood() ) {
 323              $error = $status->getMessage();
 324              $this->mainLoginForm( $error->toString() );
 325  
 326              return;
 327          }
 328  
 329          $u = $status->getValue();
 330  
 331          // Wipe the initial password and mail a temporary one
 332          $u->setPassword( null );
 333          $u->saveSettings();
 334          $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
 335  
 336          wfRunHooks( 'AddNewAccount', array( $u, true ) );
 337          $u->addNewUserLogEntry( 'byemail', $this->mReason );
 338  
 339          $out = $this->getOutput();
 340          $out->setPageTitle( $this->msg( 'accmailtitle' ) );
 341  
 342          if ( !$result->isGood() ) {
 343              $this->mainLoginForm( $this->msg( 'mailerror', $result->getWikiText() )->text() );
 344          } else {
 345              $out->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
 346              $this->executeReturnTo( 'success' );
 347          }
 348      }
 349  
 350      /**
 351       * @private
 352       * @return bool
 353       */
 354  	function addNewAccount() {
 355          global $wgContLang, $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector;
 356  
 357          # Create the account and abort if there's a problem doing so
 358          $status = $this->addNewAccountInternal();
 359          if ( !$status->isGood() ) {
 360              $error = $status->getMessage();
 361              $this->mainLoginForm( $error->toString() );
 362  
 363              return false;
 364          }
 365  
 366          $u = $status->getValue();
 367  
 368          # Only save preferences if the user is not creating an account for someone else.
 369          if ( $this->getUser()->isAnon() ) {
 370              # If we showed up language selection links, and one was in use, be
 371              # smart (and sensible) and save that language as the user's preference
 372              if ( $wgLoginLanguageSelector && $this->mLanguage ) {
 373                  $u->setOption( 'language', $this->mLanguage );
 374              } else {
 375  
 376                  # Otherwise the user's language preference defaults to $wgContLang,
 377                  # but it may be better to set it to their preferred $wgContLang variant,
 378                  # based on browser preferences or URL parameters.
 379                  $u->setOption( 'language', $wgContLang->getPreferredVariant() );
 380              }
 381              if ( $wgContLang->hasVariants() ) {
 382                  $u->setOption( 'variant', $wgContLang->getPreferredVariant() );
 383              }
 384          }
 385  
 386          $out = $this->getOutput();
 387  
 388          # Send out an email authentication message if needed
 389          if ( $wgEmailAuthentication && Sanitizer::validateEmail( $u->getEmail() ) ) {
 390              $status = $u->sendConfirmationMail();
 391              if ( $status->isGood() ) {
 392                  $out->addWikiMsg( 'confirmemail_oncreate' );
 393              } else {
 394                  $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) );
 395              }
 396          }
 397  
 398          # Save settings (including confirmation token)
 399          $u->saveSettings();
 400  
 401          # If not logged in, assume the new account as the current one and set
 402          # session cookies then show a "welcome" message or a "need cookies"
 403          # message as needed
 404          if ( $this->getUser()->isAnon() ) {
 405              $u->setCookies();
 406              $wgUser = $u;
 407              // This should set it for OutputPage and the Skin
 408              // which is needed or the personal links will be
 409              // wrong.
 410              $this->getContext()->setUser( $u );
 411              wfRunHooks( 'AddNewAccount', array( $u, false ) );
 412              $u->addNewUserLogEntry( 'create' );
 413              if ( $this->hasSessionCookie() ) {
 414                  $this->successfulCreation();
 415              } else {
 416                  $this->cookieRedirectCheck( 'new' );
 417              }
 418          } else {
 419              # Confirm that the account was created
 420              $out->setPageTitle( $this->msg( 'accountcreated' ) );
 421              $out->addWikiMsg( 'accountcreatedtext', $u->getName() );
 422              $out->addReturnTo( $this->getPageTitle() );
 423              wfRunHooks( 'AddNewAccount', array( $u, false ) );
 424              $u->addNewUserLogEntry( 'create2', $this->mReason );
 425          }
 426  
 427          return true;
 428      }
 429  
 430      /**
 431       * Make a new user account using the loaded data.
 432       * @private
 433       * @throws PermissionsError|ReadOnlyError
 434       * @return Status
 435       */
 436  	public function addNewAccountInternal() {
 437          global $wgAuth, $wgMemc, $wgAccountCreationThrottle,
 438              $wgMinimalPasswordLength, $wgEmailConfirmToEdit;
 439  
 440          // If the user passes an invalid domain, something is fishy
 441          if ( !$wgAuth->validDomain( $this->mDomain ) ) {
 442              return Status::newFatal( 'wrongpassword' );
 443          }
 444  
 445          // If we are not allowing users to login locally, we should be checking
 446          // to see if the user is actually able to authenticate to the authenti-
 447          // cation server before they create an account (otherwise, they can
 448          // create a local account and login as any domain user). We only need
 449          // to check this for domains that aren't local.
 450          if ( 'local' != $this->mDomain && $this->mDomain != '' ) {
 451              if (
 452                  !$wgAuth->canCreateAccounts() &&
 453                  (
 454                      !$wgAuth->userExists( $this->mUsername ) ||
 455                      !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
 456                  )
 457              ) {
 458                  return Status::newFatal( 'wrongpassword' );
 459              }
 460          }
 461  
 462          if ( wfReadOnly() ) {
 463              throw new ReadOnlyError;
 464          }
 465  
 466          # Request forgery checks.
 467          if ( !self::getCreateaccountToken() ) {
 468              self::setCreateaccountToken();
 469  
 470              return Status::newFatal( 'nocookiesfornew' );
 471          }
 472  
 473          # The user didn't pass a createaccount token
 474          if ( !$this->mToken ) {
 475              return Status::newFatal( 'sessionfailure' );
 476          }
 477  
 478          # Validate the createaccount token
 479          if ( $this->mToken !== self::getCreateaccountToken() ) {
 480              return Status::newFatal( 'sessionfailure' );
 481          }
 482  
 483          # Check permissions
 484          $currentUser = $this->getUser();
 485          $creationBlock = $currentUser->isBlockedFromCreateAccount();
 486          if ( !$currentUser->isAllowed( 'createaccount' ) ) {
 487              throw new PermissionsError( 'createaccount' );
 488          } elseif ( $creationBlock instanceof Block ) {
 489              // Throws an ErrorPageError.
 490              $this->userBlockedMessage( $creationBlock );
 491  
 492              // This should never be reached.
 493              return false;
 494          }
 495  
 496          # Include checks that will include GlobalBlocking (Bug 38333)
 497          $permErrors = $this->getPageTitle()->getUserPermissionsErrors(
 498              'createaccount',
 499              $currentUser,
 500              true
 501          );
 502  
 503          if ( count( $permErrors ) ) {
 504              throw new PermissionsError( 'createaccount', $permErrors );
 505          }
 506  
 507          $ip = $this->getRequest()->getIP();
 508          if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
 509              return Status::newFatal( 'sorbs_create_account_reason' );
 510          }
 511  
 512          // Normalize the name so that silly things don't cause "invalid username"
 513          // errors. User::newFromName does some rather strict checking, rejecting
 514          // e.g. leading/trailing/multiple spaces. But first we need to reject
 515          // usernames that would be treated as titles with a fragment part.
 516          if ( strpos( $this->mUsername, '#' ) !== false ) {
 517              return Status::newFatal( 'noname' );
 518          }
 519          $title = Title::makeTitleSafe( NS_USER, $this->mUsername );
 520          if ( !is_object( $title ) ) {
 521              return Status::newFatal( 'noname' );
 522          }
 523  
 524          # Now create a dummy user ($u) and check if it is valid
 525          $u = User::newFromName( $title->getText(), 'creatable' );
 526          if ( !is_object( $u ) ) {
 527              return Status::newFatal( 'noname' );
 528          } elseif ( 0 != $u->idForName() ) {
 529              return Status::newFatal( 'userexists' );
 530          }
 531  
 532          if ( $this->mCreateaccountMail ) {
 533              # do not force a password for account creation by email
 534              # set invalid password, it will be replaced later by a random generated password
 535              $this->mPassword = null;
 536          } else {
 537              if ( $this->mPassword !== $this->mRetype ) {
 538                  return Status::newFatal( 'badretype' );
 539              }
 540  
 541              # check for minimal password length
 542              $valid = $u->getPasswordValidity( $this->mPassword );
 543              if ( $valid !== true ) {
 544                  if ( !is_array( $valid ) ) {
 545                      $valid = array( $valid, $wgMinimalPasswordLength );
 546                  }
 547  
 548                  return call_user_func_array( 'Status::newFatal', $valid );
 549              }
 550          }
 551  
 552          # if you need a confirmed email address to edit, then obviously you
 553          # need an email address.
 554          if ( $wgEmailConfirmToEdit && strval( $this->mEmail ) === '' ) {
 555              return Status::newFatal( 'noemailtitle' );
 556          }
 557  
 558          if ( strval( $this->mEmail ) !== '' && !Sanitizer::validateEmail( $this->mEmail ) ) {
 559              return Status::newFatal( 'invalidemailaddress' );
 560          }
 561  
 562          # Set some additional data so the AbortNewAccount hook can be used for
 563          # more than just username validation
 564          $u->setEmail( $this->mEmail );
 565          $u->setRealName( $this->mRealName );
 566  
 567          $abortError = '';
 568          $abortStatus = null;
 569          if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError, &$abortStatus ) ) ) {
 570              // Hook point to add extra creation throttles and blocks
 571              wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
 572              if ( $abortStatus === null ) {
 573                  // Report back the old string as a raw message status.
 574                  // This will report the error back as 'createaccount-hook-aborted'
 575                  // with the given string as the message.
 576                  // To return a different error code, return a Status object.
 577                  $abortError = new Message( 'createaccount-hook-aborted', array( $abortError ) );
 578                  $abortError->text();
 579  
 580                  return Status::newFatal( $abortError );
 581              } else {
 582                  // For MediaWiki 1.23+ and updated hooks, return the Status object
 583                  // returned from the hook.
 584                  return $abortStatus;
 585              }
 586          }
 587  
 588          // Hook point to check for exempt from account creation throttle
 589          if ( !wfRunHooks( 'ExemptFromAccountCreationThrottle', array( $ip ) ) ) {
 590              wfDebug( "LoginForm::exemptFromAccountCreationThrottle: a hook " .
 591                  "allowed account creation w/o throttle\n" );
 592          } else {
 593              if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
 594                  $key = wfMemcKey( 'acctcreate', 'ip', $ip );
 595                  $value = $wgMemc->get( $key );
 596                  if ( !$value ) {
 597                      $wgMemc->set( $key, 0, 86400 );
 598                  }
 599                  if ( $value >= $wgAccountCreationThrottle ) {
 600                      return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle );
 601                  }
 602                  $wgMemc->incr( $key );
 603              }
 604          }
 605  
 606          if ( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
 607              return Status::newFatal( 'externaldberror' );
 608          }
 609  
 610          self::clearCreateaccountToken();
 611  
 612          return $this->initUser( $u, false );
 613      }
 614  
 615      /**
 616       * Actually add a user to the database.
 617       * Give it a User object that has been initialised with a name.
 618       *
 619       * @param User $u
 620       * @param bool $autocreate True if this is an autocreation via auth plugin
 621       * @return Status Status object, with the User object in the value member on success
 622       * @private
 623       */
 624  	function initUser( $u, $autocreate ) {
 625          global $wgAuth;
 626  
 627          $status = $u->addToDatabase();
 628          if ( !$status->isOK() ) {
 629              return $status;
 630          }
 631  
 632          if ( $wgAuth->allowPasswordChange() ) {
 633              $u->setPassword( $this->mPassword );
 634          }
 635  
 636          $u->setEmail( $this->mEmail );
 637          $u->setRealName( $this->mRealName );
 638          $u->setToken();
 639  
 640          $wgAuth->initUser( $u, $autocreate );
 641  
 642          $u->saveSettings();
 643  
 644          // Update user count
 645          DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
 646  
 647          // Watch user's userpage and talk page
 648          $u->addWatch( $u->getUserPage(), WatchedItem::IGNORE_USER_RIGHTS );
 649  
 650          return Status::newGood( $u );
 651      }
 652  
 653      /**
 654       * Internally authenticate the login request.
 655       *
 656       * This may create a local account as a side effect if the
 657       * authentication plugin allows transparent local account
 658       * creation.
 659       * @return int
 660       */
 661  	public function authenticateUserData() {
 662          global $wgUser, $wgAuth;
 663  
 664          $this->load();
 665  
 666          if ( $this->mUsername == '' ) {
 667              return self::NO_NAME;
 668          }
 669  
 670          // We require a login token to prevent login CSRF
 671          // Handle part of this before incrementing the throttle so
 672          // token-less login attempts don't count towards the throttle
 673          // but wrong-token attempts do.
 674  
 675          // If the user doesn't have a login token yet, set one.
 676          if ( !self::getLoginToken() ) {
 677              self::setLoginToken();
 678  
 679              return self::NEED_TOKEN;
 680          }
 681          // If the user didn't pass a login token, tell them we need one
 682          if ( !$this->mToken ) {
 683              return self::NEED_TOKEN;
 684          }
 685  
 686          $throttleCount = self::incLoginThrottle( $this->mUsername );
 687          if ( $throttleCount === true ) {
 688              return self::THROTTLED;
 689          }
 690  
 691          // Validate the login token
 692          if ( $this->mToken !== self::getLoginToken() ) {
 693              return self::WRONG_TOKEN;
 694          }
 695  
 696          // Load the current user now, and check to see if we're logging in as
 697          // the same name. This is necessary because loading the current user
 698          // (say by calling getName()) calls the UserLoadFromSession hook, which
 699          // potentially creates the user in the database. Until we load $wgUser,
 700          // checking for user existence using User::newFromName($name)->getId() below
 701          // will effectively be using stale data.
 702          if ( $this->getUser()->getName() === $this->mUsername ) {
 703              wfDebug( __METHOD__ . ": already logged in as {$this->mUsername}\n" );
 704  
 705              return self::SUCCESS;
 706          }
 707  
 708          $u = User::newFromName( $this->mUsername );
 709  
 710          // Give extensions a way to indicate the username has been updated,
 711          // rather than telling the user the account doesn't exist.
 712          if ( !wfRunHooks( 'LoginUserMigrated', array( $u, &$msg ) ) ) {
 713              $this->mAbortLoginErrorMsg = $msg;
 714              return self::USER_MIGRATED;
 715          }
 716  
 717          if ( !( $u instanceof User ) || !User::isUsableName( $u->getName() ) ) {
 718              return self::ILLEGAL;
 719          }
 720  
 721          $isAutoCreated = false;
 722          if ( $u->getID() == 0 ) {
 723              $status = $this->attemptAutoCreate( $u );
 724              if ( $status !== self::SUCCESS ) {
 725                  return $status;
 726              } else {
 727                  $isAutoCreated = true;
 728              }
 729          } else {
 730              $u->load();
 731          }
 732  
 733          // Give general extensions, such as a captcha, a chance to abort logins
 734          $abort = self::ABORTED;
 735          $msg = null;
 736          if ( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) {
 737              $this->mAbortLoginErrorMsg = $msg;
 738  
 739              return $abort;
 740          }
 741  
 742          global $wgBlockDisablesLogin;
 743          if ( !$u->checkPassword( $this->mPassword ) ) {
 744              if ( $u->checkTemporaryPassword( $this->mPassword ) ) {
 745                  // The e-mailed temporary password should not be used for actu-
 746                  // al logins; that's a very sloppy habit, and insecure if an
 747                  // attacker has a few seconds to click "search" on someone's o-
 748                  // pen mail reader.
 749                  //
 750                  // Allow it to be used only to reset the password a single time
 751                  // to a new value, which won't be in the user's e-mail ar-
 752                  // chives.
 753                  //
 754                  // For backwards compatibility, we'll still recognize it at the
 755                  // login form to minimize surprises for people who have been
 756                  // logging in with a temporary password for some time.
 757                  //
 758                  // As a side-effect, we can authenticate the user's e-mail ad-
 759                  // dress if it's not already done, since the temporary password
 760                  // was sent via e-mail.
 761                  if ( !$u->isEmailConfirmed() ) {
 762                      $u->confirmEmail();
 763                      $u->saveSettings();
 764                  }
 765  
 766                  // At this point we just return an appropriate code/ indicating
 767                  // that the UI should show a password reset form; bot inter-
 768                  // faces etc will probably just fail cleanly here.
 769                  $this->mAbortLoginErrorMsg = 'resetpass-temp-emailed';
 770                  $this->mTempPasswordUsed = true;
 771                  $retval = self::RESET_PASS;
 772              } else {
 773                  $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS;
 774              }
 775          } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) {
 776              // If we've enabled it, make it so that a blocked user cannot login
 777              $retval = self::USER_BLOCKED;
 778          } elseif ( $u->getPasswordExpired() == 'hard' ) {
 779              // Force reset now, without logging in
 780              $retval = self::RESET_PASS;
 781              $this->mAbortLoginErrorMsg = 'resetpass-expired';
 782          } else {
 783              $wgAuth->updateUser( $u );
 784              $wgUser = $u;
 785              // This should set it for OutputPage and the Skin
 786              // which is needed or the personal links will be
 787              // wrong.
 788              $this->getContext()->setUser( $u );
 789  
 790              // Please reset throttle for successful logins, thanks!
 791              if ( $throttleCount ) {
 792                  self::clearLoginThrottle( $this->mUsername );
 793              }
 794  
 795              if ( $isAutoCreated ) {
 796                  // Must be run after $wgUser is set, for correct new user log
 797                  wfRunHooks( 'AuthPluginAutoCreate', array( $u ) );
 798              }
 799  
 800              $retval = self::SUCCESS;
 801          }
 802          wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
 803  
 804          return $retval;
 805      }
 806  
 807      /**
 808       * Increment the login attempt throttle hit count for the (username,current IP)
 809       * tuple unless the throttle was already reached.
 810       * @param string $username The user name
 811       * @return bool|int The integer hit count or True if it is already at the limit
 812       */
 813  	public static function incLoginThrottle( $username ) {
 814          global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest;
 815          $username = trim( $username ); // sanity
 816  
 817          $throttleCount = 0;
 818          if ( is_array( $wgPasswordAttemptThrottle ) ) {
 819              $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
 820              $count = $wgPasswordAttemptThrottle['count'];
 821              $period = $wgPasswordAttemptThrottle['seconds'];
 822  
 823              $throttleCount = $wgMemc->get( $throttleKey );
 824              if ( !$throttleCount ) {
 825                  $wgMemc->add( $throttleKey, 1, $period ); // start counter
 826              } elseif ( $throttleCount < $count ) {
 827                  $wgMemc->incr( $throttleKey );
 828              } elseif ( $throttleCount >= $count ) {
 829                  return true;
 830              }
 831          }
 832  
 833          return $throttleCount;
 834      }
 835  
 836      /**
 837       * Clear the login attempt throttle hit count for the (username,current IP) tuple.
 838       * @param string $username The user name
 839       * @return void
 840       */
 841  	public static function clearLoginThrottle( $username ) {
 842          global $wgMemc, $wgRequest;
 843          $username = trim( $username ); // sanity
 844  
 845          $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
 846          $wgMemc->delete( $throttleKey );
 847      }
 848  
 849      /**
 850       * Attempt to automatically create a user on login. Only succeeds if there
 851       * is an external authentication method which allows it.
 852       *
 853       * @param User $user
 854       *
 855       * @return int Status code
 856       */
 857  	function attemptAutoCreate( $user ) {
 858          global $wgAuth;
 859  
 860          if ( $this->getUser()->isBlockedFromCreateAccount() ) {
 861              wfDebug( __METHOD__ . ": user is blocked from account creation\n" );
 862  
 863              return self::CREATE_BLOCKED;
 864          }
 865  
 866          if ( !$wgAuth->autoCreate() ) {
 867              return self::NOT_EXISTS;
 868          }
 869  
 870          if ( !$wgAuth->userExists( $user->getName() ) ) {
 871              wfDebug( __METHOD__ . ": user does not exist\n" );
 872  
 873              return self::NOT_EXISTS;
 874          }
 875  
 876          if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
 877              wfDebug( __METHOD__ . ": \$wgAuth->authenticate() returned false, aborting\n" );
 878  
 879              return self::WRONG_PLUGIN_PASS;
 880          }
 881  
 882          $abortError = '';
 883          if ( !wfRunHooks( 'AbortAutoAccount', array( $user, &$abortError ) ) ) {
 884              // Hook point to add extra creation throttles and blocks
 885              wfDebug( "LoginForm::attemptAutoCreate: a hook blocked creation: $abortError\n" );
 886              $this->mAbortLoginErrorMsg = $abortError;
 887  
 888              return self::ABORTED;
 889          }
 890  
 891          wfDebug( __METHOD__ . ": creating account\n" );
 892          $status = $this->initUser( $user, true );
 893  
 894          if ( !$status->isOK() ) {
 895              $errors = $status->getErrorsByType( 'error' );
 896              $this->mAbortLoginErrorMsg = $errors[0]['message'];
 897  
 898              return self::ABORTED;
 899          }
 900  
 901          return self::SUCCESS;
 902      }
 903  
 904  	function processLogin() {
 905          global $wgMemc, $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle,
 906              $wgInvalidPasswordReset;
 907  
 908          switch ( $this->authenticateUserData() ) {
 909              case self::SUCCESS:
 910                  # We've verified now, update the real record
 911                  $user = $this->getUser();
 912                  $user->invalidateCache();
 913  
 914                  if ( $user->requiresHTTPS() ) {
 915                      $this->mStickHTTPS = true;
 916                  }
 917  
 918                  if ( $wgSecureLogin && !$this->mStickHTTPS ) {
 919                      $user->setCookies( $this->mRequest, false, $this->mRemember );
 920                  } else {
 921                      $user->setCookies( $this->mRequest, null, $this->mRemember );
 922                  }
 923                  self::clearLoginToken();
 924  
 925                  // Reset the throttle
 926                  $request = $this->getRequest();
 927                  $key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) );
 928                  $wgMemc->delete( $key );
 929  
 930                  if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
 931                      /* Replace the language object to provide user interface in
 932                       * correct language immediately on this first page load.
 933                       */
 934                      $code = $request->getVal( 'uselang', $user->getOption( 'language' ) );
 935                      $userLang = Language::factory( $code );
 936                      $wgLang = $userLang;
 937                      $this->getContext()->setLanguage( $userLang );
 938                      // Reset SessionID on Successful login (bug 40995)
 939                      $this->renewSessionId();
 940                      if ( $this->getUser()->getPasswordExpired() == 'soft' ) {
 941                          $this->resetLoginForm( $this->msg( 'resetpass-expired-soft' ) );
 942                      } elseif ( $wgInvalidPasswordReset
 943                          && !$user->isValidPassword( $this->mPassword )
 944                      ) {
 945                          $status = $user->checkPasswordValidity( $this->mPassword );
 946                          $this->resetLoginForm(
 947                              $status->getMessage( 'resetpass-validity-soft' )
 948                          );
 949                      } else {
 950                          $this->successfulLogin();
 951                      }
 952                  } else {
 953                      $this->cookieRedirectCheck( 'login' );
 954                  }
 955                  break;
 956  
 957              case self::NEED_TOKEN:
 958                  $error = $this->mAbortLoginErrorMsg ?: 'nocookiesforlogin';
 959                  $this->mainLoginForm( $this->msg( $error )->parse() );
 960                  break;
 961              case self::WRONG_TOKEN:
 962                  $error = $this->mAbortLoginErrorMsg ?: 'sessionfailure';
 963                  $this->mainLoginForm( $this->msg( $error )->text() );
 964                  break;
 965              case self::NO_NAME:
 966              case self::ILLEGAL:
 967                  $error = $this->mAbortLoginErrorMsg ?: 'noname';
 968                  $this->mainLoginForm( $this->msg( $error )->text() );
 969                  break;
 970              case self::WRONG_PLUGIN_PASS:
 971                  $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
 972                  $this->mainLoginForm( $this->msg( $error )->text() );
 973                  break;
 974              case self::NOT_EXISTS:
 975                  if ( $this->getUser()->isAllowed( 'createaccount' ) ) {
 976                      $error = $this->mAbortLoginErrorMsg ?: 'nosuchuser';
 977                      $this->mainLoginForm( $this->msg( $error,
 978                          wfEscapeWikiText( $this->mUsername ) )->parse() );
 979                  } else {
 980                      $error = $this->mAbortLoginErrorMsg ?: 'nosuchusershort';
 981                      $this->mainLoginForm( $this->msg( $error,
 982                          wfEscapeWikiText( $this->mUsername ) )->text() );
 983                  }
 984                  break;
 985              case self::WRONG_PASS:
 986                  $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
 987                  $this->mainLoginForm( $this->msg( $error )->text() );
 988                  break;
 989              case self::EMPTY_PASS:
 990                  $error = $this->mAbortLoginErrorMsg ?: 'wrongpasswordempty';
 991                  $this->mainLoginForm( $this->msg( $error )->text() );
 992                  break;
 993              case self::RESET_PASS:
 994                  $error = $this->mAbortLoginErrorMsg ?: 'resetpass_announce';
 995                  $this->resetLoginForm( $this->msg( $error ) );
 996                  break;
 997              case self::CREATE_BLOCKED:
 998                  $this->userBlockedMessage( $this->getUser()->isBlockedFromCreateAccount() );
 999                  break;
1000              case self::THROTTLED:
1001                  $error = $this->mAbortLoginErrorMsg ?: 'login-throttled';
1002                  $this->mainLoginForm( $this->msg( $error )
1003                      ->params( $this->getLanguage()->formatDuration( $wgPasswordAttemptThrottle['seconds'] ) )
1004                      ->text()
1005                  );
1006                  break;
1007              case self::USER_BLOCKED:
1008                  $error = $this->mAbortLoginErrorMsg ?: 'login-userblocked';
1009                  $this->mainLoginForm( $this->msg( $error, $this->mUsername )->escaped() );
1010                  break;
1011              case self::ABORTED:
1012                  $error = $this->mAbortLoginErrorMsg ?: 'login-abort-generic';
1013                  $this->mainLoginForm( $this->msg( $error,
1014                          wfEscapeWikiText( $this->mUsername ) )->text() );
1015                  break;
1016              case self::USER_MIGRATED:
1017                  $error = $this->mAbortLoginErrorMsg ?: 'login-migrated-generic';
1018                  $params = array();
1019                  if ( is_array( $error ) ) {
1020                      $error = array_shift( $this->mAbortLoginErrorMsg );
1021                      $params = $this->mAbortLoginErrorMsg;
1022                  }
1023                  $this->mainLoginForm( $this->msg( $error, $params )->text() );
1024                  break;
1025              default:
1026                  throw new MWException( 'Unhandled case value' );
1027          }
1028      }
1029  
1030      /**
1031       * Show the Special:ChangePassword form, with custom message
1032       * @param Message $msg
1033       */
1034  	protected function resetLoginForm( Message $msg ) {
1035          // Allow hooks to explain this password reset in more detail
1036          wfRunHooks( 'LoginPasswordResetMessage', array( &$msg, $this->mUsername ) );
1037          $reset = new SpecialChangePassword();
1038          $derivative = new DerivativeContext( $this->getContext() );
1039          $derivative->setTitle( $reset->getPageTitle() );
1040          $reset->setContext( $derivative );
1041          if ( !$this->mTempPasswordUsed ) {
1042              $reset->setOldPasswordMessage( 'oldpassword' );
1043          }
1044          $reset->setChangeMessage( $msg );
1045          $reset->execute( null );
1046      }
1047  
1048      /**
1049       * @param User $u
1050       * @param bool $throttle
1051       * @param string $emailTitle Message name of email title
1052       * @param string $emailText Message name of email text
1053       * @return Status
1054       */
1055  	function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle',
1056          $emailText = 'passwordremindertext'
1057      ) {
1058          global $wgNewPasswordExpiry;
1059  
1060          if ( $u->getEmail() == '' ) {
1061              return Status::newFatal( 'noemail', $u->getName() );
1062          }
1063          $ip = $this->getRequest()->getIP();
1064          if ( !$ip ) {
1065              return Status::newFatal( 'badipaddress' );
1066          }
1067  
1068          $currentUser = $this->getUser();
1069          wfRunHooks( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) );
1070  
1071          $np = $u->randomPassword();
1072          $u->setNewpassword( $np, $throttle );
1073          $u->saveSettings();
1074          $userLanguage = $u->getOption( 'language' );
1075  
1076          $mainPage = Title::newMainPage();
1077          $mainPageUrl = $mainPage->getCanonicalURL();
1078  
1079          $m = $this->msg( $emailText, $ip, $u->getName(), $np, '<' . $mainPageUrl . '>',
1080              round( $wgNewPasswordExpiry / 86400 ) )->inLanguage( $userLanguage )->text();
1081          $result = $u->sendMail( $this->msg( $emailTitle )->inLanguage( $userLanguage )->text(), $m );
1082  
1083          return $result;
1084      }
1085  
1086      /**
1087       * Run any hooks registered for logins, then HTTP redirect to
1088       * $this->mReturnTo (or Main Page if that's undefined).  Formerly we had a
1089       * nice message here, but that's really not as useful as just being sent to
1090       * wherever you logged in from.  It should be clear that the action was
1091       * successful, given the lack of error messages plus the appearance of your
1092       * name in the upper right.
1093       *
1094       * @private
1095       */
1096  	function successfulLogin() {
1097          # Run any hooks; display injected HTML if any, else redirect
1098          $currentUser = $this->getUser();
1099          $injected_html = '';
1100          wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
1101  
1102          if ( $injected_html !== '' ) {
1103              $this->displaySuccessfulAction( 'success', $this->msg( 'loginsuccesstitle' ),
1104                  'loginsuccess', $injected_html );
1105          } else {
1106              $this->executeReturnTo( 'successredirect' );
1107          }
1108      }
1109  
1110      /**
1111       * Run any hooks registered for logins, then display a message welcoming
1112       * the user.
1113       *
1114       * @private
1115       */
1116  	function successfulCreation() {
1117          # Run any hooks; display injected HTML
1118          $currentUser = $this->getUser();
1119          $injected_html = '';
1120          $welcome_creation_msg = 'welcomecreation-msg';
1121  
1122          wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
1123  
1124          /**
1125           * Let any extensions change what message is shown.
1126           * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeWelcomeCreation
1127           * @since 1.18
1128           */
1129          wfRunHooks( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) );
1130  
1131          $this->displaySuccessfulAction(
1132              'signup',
1133              $this->msg( 'welcomeuser', $this->getUser()->getName() ),
1134              $welcome_creation_msg, $injected_html
1135          );
1136      }
1137  
1138      /**
1139       * Display a "successful action" page.
1140       *
1141       * @param string $type Condition of return to; see `executeReturnTo`
1142       * @param string|Message $title Page's title
1143       * @param string $msgname
1144       * @param string $injected_html
1145       */
1146  	private function displaySuccessfulAction( $type, $title, $msgname, $injected_html ) {
1147          $out = $this->getOutput();
1148          $out->setPageTitle( $title );
1149          if ( $msgname ) {
1150              $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
1151          }
1152  
1153          $out->addHTML( $injected_html );
1154  
1155          $this->executeReturnTo( $type );
1156      }
1157  
1158      /**
1159       * Output a message that informs the user that they cannot create an account because
1160       * there is a block on them or their IP which prevents account creation.  Note that
1161       * User::isBlockedFromCreateAccount(), which gets this block, ignores the 'hardblock'
1162       * setting on blocks (bug 13611).
1163       * @param Block $block The block causing this error
1164       * @throws ErrorPageError
1165       */
1166  	function userBlockedMessage( Block $block ) {
1167          # Let's be nice about this, it's likely that this feature will be used
1168          # for blocking large numbers of innocent people, e.g. range blocks on
1169          # schools. Don't blame it on the user. There's a small chance that it
1170          # really is the user's fault, i.e. the username is blocked and they
1171          # haven't bothered to log out before trying to create an account to
1172          # evade it, but we'll leave that to their guilty conscience to figure
1173          # out.
1174          $errorParams = array(
1175              $block->getTarget(),
1176              $block->mReason ? $block->mReason : $this->msg( 'blockednoreason' )->text(),
1177              $block->getByName()
1178          );
1179  
1180          if ( $block->getType() === Block::TYPE_RANGE ) {
1181              $errorMessage = 'cantcreateaccount-range-text';
1182              $errorParams[] = $this->getRequest()->getIP();
1183          } else {
1184              $errorMessage = 'cantcreateaccount-text';
1185          }
1186  
1187          throw new ErrorPageError(
1188              'cantcreateaccounttitle',
1189              $errorMessage,
1190              $errorParams
1191          );
1192      }
1193  
1194      /**
1195       * Add a "return to" link or redirect to it.
1196       * Extensions can use this to reuse the "return to" logic after
1197       * inject steps (such as redirection) into the login process.
1198       *
1199       * @param string $type One of the following:
1200       *    - error: display a return to link ignoring $wgRedirectOnLogin
1201       *    - signup: display a return to link using $wgRedirectOnLogin if needed
1202       *    - success: display a return to link using $wgRedirectOnLogin if needed
1203       *    - successredirect: send an HTTP redirect using $wgRedirectOnLogin if needed
1204       * @param string $returnTo
1205       * @param array|string $returnToQuery
1206       * @param bool $stickHTTPs Keep redirect link on HTTPs
1207       * @since 1.22
1208       */
1209  	public function showReturnToPage(
1210          $type, $returnTo = '', $returnToQuery = '', $stickHTTPs = false
1211      ) {
1212          $this->mReturnTo = $returnTo;
1213          $this->mReturnToQuery = $returnToQuery;
1214          $this->mStickHTTPS = $stickHTTPs;
1215          $this->executeReturnTo( $type );
1216      }
1217  
1218      /**
1219       * Add a "return to" link or redirect to it.
1220       *
1221       * @param string $type One of the following:
1222       *    - error: display a return to link ignoring $wgRedirectOnLogin
1223       *    - signup: display a return to link using $wgRedirectOnLogin if needed
1224       *    - success: display a return to link using $wgRedirectOnLogin if needed
1225       *    - successredirect: send an HTTP redirect using $wgRedirectOnLogin if needed
1226       */
1227  	private function executeReturnTo( $type ) {
1228          global $wgRedirectOnLogin, $wgSecureLogin;
1229  
1230          if ( $type != 'error' && $wgRedirectOnLogin !== null ) {
1231              $returnTo = $wgRedirectOnLogin;
1232              $returnToQuery = array();
1233          } else {
1234              $returnTo = $this->mReturnTo;
1235              $returnToQuery = wfCgiToArray( $this->mReturnToQuery );
1236          }
1237  
1238          // Allow modification of redirect behavior
1239          wfRunHooks( 'PostLoginRedirect', array( &$returnTo, &$returnToQuery, &$type ) );
1240  
1241          $returnToTitle = Title::newFromText( $returnTo );
1242          if ( !$returnToTitle ) {
1243              $returnToTitle = Title::newMainPage();
1244          }
1245  
1246          if ( $wgSecureLogin && !$this->mStickHTTPS ) {
1247              $options = array( 'http' );
1248              $proto = PROTO_HTTP;
1249          } elseif ( $wgSecureLogin ) {
1250              $options = array( 'https' );
1251              $proto = PROTO_HTTPS;
1252          } else {
1253              $options = array();
1254              $proto = PROTO_RELATIVE;
1255          }
1256  
1257          if ( $type == 'successredirect' ) {
1258              $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto );
1259              $this->getOutput()->redirect( $redirectUrl );
1260          } else {
1261              $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options );
1262          }
1263      }
1264  
1265      /**
1266       * @param string $msg
1267       * @param string $msgtype
1268       * @private
1269       */
1270  	function mainLoginForm( $msg, $msgtype = 'error' ) {
1271          global $wgEnableEmail, $wgEnableUserEmail;
1272          global $wgHiddenPrefs, $wgLoginLanguageSelector;
1273          global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
1274          global $wgSecureLogin, $wgPasswordResetRoutes;
1275  
1276          $titleObj = $this->getPageTitle();
1277          $user = $this->getUser();
1278          $out = $this->getOutput();
1279  
1280          if ( $this->mType == 'signup' ) {
1281              // Block signup here if in readonly. Keeps user from
1282              // going through the process (filling out data, etc)
1283              // and being informed later.
1284              $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $user, true );
1285              if ( count( $permErrors ) ) {
1286                  throw new PermissionsError( 'createaccount', $permErrors );
1287              } elseif ( $user->isBlockedFromCreateAccount() ) {
1288                  $this->userBlockedMessage( $user->isBlockedFromCreateAccount() );
1289  
1290                  return;
1291              } elseif ( wfReadOnly() ) {
1292                  throw new ReadOnlyError;
1293              }
1294          }
1295  
1296          // Pre-fill username (if not creating an account, bug 44775).
1297          if ( $this->mUsername == '' && $this->mType != 'signup' ) {
1298              if ( $user->isLoggedIn() ) {
1299                  $this->mUsername = $user->getName();
1300              } else {
1301                  $this->mUsername = $this->getRequest()->getCookie( 'UserName' );
1302              }
1303          }
1304  
1305          // Generic styles and scripts for both login and signup form
1306          $out->addModuleStyles( array(
1307              'mediawiki.ui',
1308              'mediawiki.ui.button',
1309              'mediawiki.ui.checkbox',
1310              'mediawiki.ui.input',
1311              'mediawiki.special.userlogin.common.styles'
1312          ) );
1313          $out->addModules( array(
1314              'mediawiki.special.userlogin.common.js'
1315          ) );
1316  
1317          if ( $this->mType == 'signup' ) {
1318              // XXX hack pending RL or JS parse() support for complex content messages
1319              // https://bugzilla.wikimedia.org/show_bug.cgi?id=25349
1320              $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
1321                  $this->msg( 'createacct-imgcaptcha-help' )->parse() );
1322  
1323              // Additional styles and scripts for signup form
1324              $out->addModules( array(
1325                  'mediawiki.special.userlogin.signup.js'
1326              ) );
1327              $out->addModuleStyles( array(
1328                  'mediawiki.special.userlogin.signup.styles'
1329              ) );
1330  
1331              $template = new UsercreateTemplate();
1332  
1333              // Must match number of benefits defined in messages
1334              $template->set( 'benefitCount', 3 );
1335  
1336              $q = 'action=submitlogin&type=signup';
1337              $linkq = 'type=login';
1338          } else {
1339              // Additional styles for login form
1340              $out->addModuleStyles( array(
1341                  'mediawiki.special.userlogin.login.styles'
1342              ) );
1343  
1344              $template = new UserloginTemplate();
1345  
1346              $q = 'action=submitlogin&type=login';
1347              $linkq = 'type=signup';
1348          }
1349  
1350          if ( $this->mReturnTo !== '' ) {
1351              $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
1352              if ( $this->mReturnToQuery !== '' ) {
1353                  $returnto .= '&returntoquery=' .
1354                      wfUrlencode( $this->mReturnToQuery );
1355              }
1356              $q .= $returnto;
1357              $linkq .= $returnto;
1358          }
1359  
1360          # Don't show a "create account" link if the user can't.
1361          if ( $this->showCreateOrLoginLink( $user ) ) {
1362              # Pass any language selection on to the mode switch link
1363              if ( $wgLoginLanguageSelector && $this->mLanguage ) {
1364                  $linkq .= '&uselang=' . $this->mLanguage;
1365              }
1366              // Supply URL, login template creates the button.
1367              $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
1368          } else {
1369              $template->set( 'link', '' );
1370          }
1371  
1372          $resetLink = $this->mType == 'signup'
1373              ? null
1374              : is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) );
1375  
1376          $template->set( 'header', '' );
1377          $template->set( 'skin', $this->getSkin() );
1378          $template->set( 'name', $this->mUsername );
1379          $template->set( 'password', $this->mPassword );
1380          $template->set( 'retype', $this->mRetype );
1381          $template->set( 'createemailset', $this->mCreateaccountMail );
1382          $template->set( 'email', $this->mEmail );
1383          $template->set( 'realname', $this->mRealName );
1384          $template->set( 'domain', $this->mDomain );
1385          $template->set( 'reason', $this->mReason );
1386  
1387          $template->set( 'action', $titleObj->getLocalURL( $q ) );
1388          $template->set( 'message', $msg );
1389          $template->set( 'messagetype', $msgtype );
1390          $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() );
1391          $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) );
1392          $template->set( 'useemail', $wgEnableEmail );
1393          $template->set( 'emailrequired', $wgEmailConfirmToEdit );
1394          $template->set( 'emailothers', $wgEnableUserEmail );
1395          $template->set( 'canreset', $wgAuth->allowPasswordChange() );
1396          $template->set( 'resetlink', $resetLink );
1397          $template->set( 'canremember', ( $wgCookieExpiration > 0 ) );
1398          $template->set( 'usereason', $user->isLoggedIn() );
1399          $template->set( 'remember', $this->mRemember );
1400          $template->set( 'cansecurelogin', ( $wgSecureLogin === true ) );
1401          $template->set( 'stickhttps', (int)$this->mStickHTTPS );
1402          $template->set( 'loggedin', $user->isLoggedIn() );
1403          $template->set( 'loggedinuser', $user->getName() );
1404  
1405          if ( $this->mType == 'signup' ) {
1406              if ( !self::getCreateaccountToken() ) {
1407                  self::setCreateaccountToken();
1408              }
1409              $template->set( 'token', self::getCreateaccountToken() );
1410          } else {
1411              if ( !self::getLoginToken() ) {
1412                  self::setLoginToken();
1413              }
1414              $template->set( 'token', self::getLoginToken() );
1415          }
1416  
1417          # Prepare language selection links as needed
1418          if ( $wgLoginLanguageSelector ) {
1419              $template->set( 'languages', $this->makeLanguageSelector() );
1420              if ( $this->mLanguage ) {
1421                  $template->set( 'uselang', $this->mLanguage );
1422              }
1423          }
1424  
1425          $template->set( 'secureLoginUrl', $this->mSecureLoginUrl );
1426          // Use loginend-https for HTTPS requests if it's not blank, loginend otherwise
1427          // Ditto for signupend.  New forms use neither.
1428          $usingHTTPS = $this->mRequest->getProtocol() == 'https';
1429          $loginendHTTPS = $this->msg( 'loginend-https' );
1430          $signupendHTTPS = $this->msg( 'signupend-https' );
1431          if ( $usingHTTPS && !$loginendHTTPS->isBlank() ) {
1432              $template->set( 'loginend', $loginendHTTPS->parse() );
1433          } else {
1434              $template->set( 'loginend', $this->msg( 'loginend' )->parse() );
1435          }
1436          if ( $usingHTTPS && !$signupendHTTPS->isBlank() ) {
1437              $template->set( 'signupend', $signupendHTTPS->parse() );
1438          } else {
1439              $template->set( 'signupend', $this->msg( 'signupend' )->parse() );
1440          }
1441  
1442          // Give authentication and captcha plugins a chance to modify the form
1443          $wgAuth->modifyUITemplate( $template, $this->mType );
1444          if ( $this->mType == 'signup' ) {
1445              wfRunHooks( 'UserCreateForm', array( &$template ) );
1446          } else {
1447              wfRunHooks( 'UserLoginForm', array( &$template ) );
1448          }
1449  
1450          $out->disallowUserJs(); // just in case...
1451          $out->addTemplate( $template );
1452      }
1453  
1454      /**
1455       * Whether the login/create account form should display a link to the
1456       * other form (in addition to whatever the skin provides).
1457       *
1458       * @param User $user
1459       * @return bool
1460       */
1461  	private function showCreateOrLoginLink( &$user ) {
1462          if ( $this->mType == 'signup' ) {
1463              return true;
1464          } elseif ( $user->isAllowed( 'createaccount' ) ) {
1465              return true;
1466          } else {
1467              return false;
1468          }
1469      }
1470  
1471      /**
1472       * Check if a session cookie is present.
1473       *
1474       * This will not pick up a cookie set during _this_ request, but is meant
1475       * to ensure that the client is returning the cookie which was set on a
1476       * previous pass through the system.
1477       *
1478       * @private
1479       * @return bool
1480       */
1481  	function hasSessionCookie() {
1482          global $wgDisableCookieCheck;
1483  
1484          return $wgDisableCookieCheck ? true : $this->getRequest()->checkSessionCookie();
1485      }
1486  
1487      /**
1488       * Get the login token from the current session
1489       * @return mixed
1490       */
1491  	public static function getLoginToken() {
1492          global $wgRequest;
1493  
1494          return $wgRequest->getSessionData( 'wsLoginToken' );
1495      }
1496  
1497      /**
1498       * Randomly generate a new login token and attach it to the current session
1499       */
1500  	public static function setLoginToken() {
1501          global $wgRequest;
1502          // Generate a token directly instead of using $user->getEditToken()
1503          // because the latter reuses $_SESSION['wsEditToken']
1504          $wgRequest->setSessionData( 'wsLoginToken', MWCryptRand::generateHex( 32 ) );
1505      }
1506  
1507      /**
1508       * Remove any login token attached to the current session
1509       */
1510  	public static function clearLoginToken() {
1511          global $wgRequest;
1512          $wgRequest->setSessionData( 'wsLoginToken', null );
1513      }
1514  
1515      /**
1516       * Get the createaccount token from the current session
1517       * @return mixed
1518       */
1519  	public static function getCreateaccountToken() {
1520          global $wgRequest;
1521  
1522          return $wgRequest->getSessionData( 'wsCreateaccountToken' );
1523      }
1524  
1525      /**
1526       * Randomly generate a new createaccount token and attach it to the current session
1527       */
1528  	public static function setCreateaccountToken() {
1529          global $wgRequest;
1530          $wgRequest->setSessionData( 'wsCreateaccountToken', MWCryptRand::generateHex( 32 ) );
1531      }
1532  
1533      /**
1534       * Remove any createaccount token attached to the current session
1535       */
1536  	public static function clearCreateaccountToken() {
1537          global $wgRequest;
1538          $wgRequest->setSessionData( 'wsCreateaccountToken', null );
1539      }
1540  
1541      /**
1542       * Renew the user's session id, using strong entropy
1543       */
1544  	private function renewSessionId() {
1545          global $wgSecureLogin, $wgCookieSecure;
1546          if ( $wgSecureLogin && !$this->mStickHTTPS ) {
1547              $wgCookieSecure = false;
1548          }
1549  
1550          wfResetSessionID();
1551      }
1552  
1553      /**
1554       * @param string $type
1555       * @private
1556       */
1557  	function cookieRedirectCheck( $type ) {
1558          $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
1559          $query = array( 'wpCookieCheck' => $type );
1560          if ( $this->mReturnTo !== '' ) {
1561              $query['returnto'] = $this->mReturnTo;
1562              $query['returntoquery'] = $this->mReturnToQuery;
1563          }
1564          $check = $titleObj->getFullURL( $query );
1565  
1566          $this->getOutput()->redirect( $check );
1567      }
1568  
1569      /**
1570       * @param string $type
1571       * @private
1572       */
1573  	function onCookieRedirectCheck( $type ) {
1574          if ( !$this->hasSessionCookie() ) {
1575              if ( $type == 'new' ) {
1576                  $this->mainLoginForm( $this->msg( 'nocookiesnew' )->parse() );
1577              } elseif ( $type == 'login' ) {
1578                  $this->mainLoginForm( $this->msg( 'nocookieslogin' )->parse() );
1579              } else {
1580                  # shouldn't happen
1581                  $this->mainLoginForm( $this->msg( 'error' )->text() );
1582              }
1583          } else {
1584              $this->successfulLogin();
1585          }
1586      }
1587  
1588      /**
1589       * Produce a bar of links which allow the user to select another language
1590       * during login/registration but retain "returnto"
1591       *
1592       * @return string
1593       */
1594  	function makeLanguageSelector() {
1595          $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
1596          if ( !$msg->isBlank() ) {
1597              $langs = explode( "\n", $msg->text() );
1598              $links = array();
1599              foreach ( $langs as $lang ) {
1600                  $lang = trim( $lang, '* ' );
1601                  $parts = explode( '|', $lang );
1602                  if ( count( $parts ) >= 2 ) {
1603                      $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1604                  }
1605              }
1606  
1607              return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
1608                  $this->getLanguage()->pipeList( $links ) )->escaped() : '';
1609          } else {
1610              return '';
1611          }
1612      }
1613  
1614      /**
1615       * Create a language selector link for a particular language
1616       * Links back to this page preserving type and returnto
1617       *
1618       * @param string $text Link text
1619       * @param string $lang Language code
1620       * @return string
1621       */
1622  	function makeLanguageSelectorLink( $text, $lang ) {
1623          if ( $this->getLanguage()->getCode() == $lang ) {
1624              // no link for currently used language
1625              return htmlspecialchars( $text );
1626          }
1627          $query = array( 'uselang' => $lang );
1628          if ( $this->mType == 'signup' ) {
1629              $query['type'] = 'signup';
1630          }
1631          if ( $this->mReturnTo !== '' ) {
1632              $query['returnto'] = $this->mReturnTo;
1633              $query['returntoquery'] = $this->mReturnToQuery;
1634          }
1635  
1636          $attr = array();
1637          $targetLanguage = Language::factory( $lang );
1638          $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
1639  
1640          return Linker::linkKnown(
1641              $this->getPageTitle(),
1642              htmlspecialchars( $text ),
1643              $attr,
1644              $query
1645          );
1646      }
1647  
1648  	protected function getGroupName() {
1649          return 'login';
1650      }
1651  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1