r56148 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r56147‎ | r56148 | r56149 >
Date:20:05, 10 September 2009
Author:happy-melon
Status:deferred
Tags:
Comment:
Reimplement new login stuff (I see the cookie loop now, not sure why I didn't before...)
Modified paths:
  • /branches/happy-melon/phase3/includes/AuthPlugin.php (modified) (history)
  • /branches/happy-melon/phase3/includes/AutoLoader.php (modified) (history)
  • /branches/happy-melon/phase3/includes/Login.php (added) (history)
  • /branches/happy-melon/phase3/includes/SpecialPage.php (modified) (history)
  • /branches/happy-melon/phase3/includes/api/ApiLogin.php (modified) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php (added) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialResetpass.php (modified) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialUserlogin.php (modified) (history)
  • /branches/happy-melon/phase3/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: branches/happy-melon/phase3/includes/api/ApiLogin.php
@@ -60,7 +60,7 @@
6161 'wpName' => $params['name'],
6262 'wpPassword' => $params['password'],
6363 'wpDomain' => $params['domain'],
64 - 'wpRemember' => ''
 64+ 'wpRemember' => '1'
6565 ));
6666
6767 // Init session if necessary
@@ -68,19 +68,11 @@
6969 wfSetupSession();
7070 }
7171
72 - $loginForm = new LoginForm($req);
73 - switch ($authRes = $loginForm->authenticateUserData()) {
74 - case LoginForm :: SUCCESS :
 72+ $loginForm = new Login( $req );
 73+ switch ( $authRes = $loginForm->attemptLogin() ) {
 74+ case Login::SUCCESS :
7575 global $wgUser, $wgCookiePrefix;
7676
77 - $wgUser->setOption('rememberpassword', 1);
78 - $wgUser->setCookies();
79 -
80 - // Run hooks. FIXME: split back and frontend from this hook.
81 - // FIXME: This hook should be placed in the backend
82 - $injected_html = '';
83 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
84 -
8577 $result['result'] = 'Success';
8678 $result['lguserid'] = intval($wgUser->getId());
8779 $result['lgusername'] = $wgUser->getName();
@@ -89,35 +81,35 @@
9082 $result['sessionid'] = session_id();
9183 break;
9284
93 - case LoginForm :: NO_NAME :
 85+ case Login::NO_NAME :
9486 $result['result'] = 'NoName';
9587 break;
96 - case LoginForm :: ILLEGAL :
 88+ case Login::ILLEGAL :
9789 $result['result'] = 'Illegal';
9890 break;
99 - case LoginForm :: WRONG_PLUGIN_PASS :
 91+ case Login::WRONG_PLUGIN_PASS :
10092 $result['result'] = 'WrongPluginPass';
10193 break;
102 - case LoginForm :: NOT_EXISTS :
 94+ case Login::NOT_EXISTS :
10395 $result['result'] = 'NotExists';
10496 break;
105 - case LoginForm :: WRONG_PASS :
 97+ case Login::WRONG_PASS :
10698 $result['result'] = 'WrongPass';
10799 break;
108 - case LoginForm :: EMPTY_PASS :
 100+ case Login::EMPTY_PASS :
109101 $result['result'] = 'EmptyPass';
110102 break;
111 - case LoginForm :: CREATE_BLOCKED :
 103+ case Login::CREATE_BLOCKED :
112104 $result['result'] = 'CreateBlocked';
113105 $result['details'] = 'Your IP address is blocked from account creation';
114106 break;
115 - case LoginForm :: THROTTLED :
 107+ case Login::THROTTLED :
116108 global $wgPasswordAttemptThrottle;
117109 $result['result'] = 'Throttled';
118110 $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']);
119111 break;
120112 default :
121 - ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
 113+ ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
122114 }
123115
124116 $this->getResult()->addValue(null, 'login', $result);
Index: branches/happy-melon/phase3/includes/AutoLoader.php
@@ -136,6 +136,8 @@
137137 'LinksUpdate' => 'includes/LinksUpdate.php',
138138 'LocalisationCache' => 'includes/LocalisationCache.php',
139139 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
 140+ 'LoginForm' => 'includes/Login.php', # For B/C
 141+ 'Login' => 'includes/Login.php',
140142 'LogPage' => 'includes/LogPage.php',
141143 'LogPager' => 'includes/LogEventsList.php',
142144 'LogEventsList' => 'includes/LogEventsList.php',
@@ -495,6 +497,7 @@
496498 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
497499 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
498500 'ContribsPager' => 'includes/specials/SpecialContributions.php',
 501+ 'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
499502 'DBLockForm' => 'includes/specials/SpecialLockdb.php',
500503 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
501504 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
@@ -515,7 +518,6 @@
516519 'ImportStringSource' => 'includes/Import.php',
517520 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
518521 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
519 - 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
520522 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
521523 'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
522524 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
@@ -564,6 +566,7 @@
565567 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
566568 'UploadForm' => 'includes/specials/SpecialUpload.php',
567569 'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
 570+ 'SpecialUserLogin' => 'includes/specials/SpecialUserlogin.php',
568571 'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
569572 'UsersPager' => 'includes/specials/SpecialListusers.php',
570573 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
Index: branches/happy-melon/phase3/includes/AuthPlugin.php
@@ -62,12 +62,13 @@
6363 /**
6464 * Modify options in the login template.
6565 *
66 - * @param $template UserLoginTemplate object.
67 - * @param $type String 'signup' or 'login'.
 66+ * @param $sp SpecialUserlogin or SpecialCreateAccount object.
 67+ * @param $type String 'signup' or 'login'. Redundant because
 68+ * you can just use instanceof to tell the two cases apart.
6869 */
69 - public function modifyUITemplate( &$template, &$type ) {
 70+ public function modifyUITemplate( &$sp, $type=null ) {
7071 # Override this!
71 - $template->set( 'usedomain', false );
 72+ $sp->mDomains = false;
7273 }
7374
7475 /**
Index: branches/happy-melon/phase3/includes/Login.php
@@ -0,0 +1,378 @@
 2+<?php
 3+
 4+/**
 5+ * Encapsulates the backend activities of logging a user into the wiki.
 6+ */
 7+class Login {
 8+
 9+ const SUCCESS = 0;
 10+ const NO_NAME = 1;
 11+ const ILLEGAL = 2;
 12+ const WRONG_PLUGIN_PASS = 3;
 13+ const NOT_EXISTS = 4;
 14+ const WRONG_PASS = 5;
 15+ const EMPTY_PASS = 6;
 16+ const RESET_PASS = 7;
 17+ const ABORTED = 8;
 18+ const CREATE_BLOCKED = 9;
 19+ const THROTTLED = 10;
 20+
 21+ const MAIL_READ_ONLY = 11;
 22+ const MAIL_PASSCHANGE_FORBIDDEN = 12;
 23+ const MAIL_BLOCKED = 13;
 24+ const MAIL_PING_THROTTLED = 14;
 25+ const MAIL_PASS_THROTTLED = 15;
 26+ const MAIL_EMPTY_EMAIL = 16;
 27+ const MAIL_BAD_IP = 17;
 28+ const MAIL_ERROR = 18;
 29+
 30+ var $mName, $mPassword, $mPosted;
 31+ var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
 32+
 33+ private $mExtUser = null;
 34+
 35+ public $mUser;
 36+ public $mMailResult;
 37+
 38+ /**
 39+ * Constructor
 40+ * @param WebRequest $request A WebRequest object passed by reference.
 41+ * uses $wgRequest if not given.
 42+ */
 43+ public function __construct( &$request=null ) {
 44+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 45+ if( !$request ) $request = &$wgRequest;
 46+
 47+ $this->mName = $request->getText( 'wpName' );
 48+ $this->mPassword = $request->getText( 'wpPassword' );
 49+ $this->mDomain = $request->getText( 'wpDomain' );
 50+ $this->mPosted = $request->wasPosted();
 51+ $this->mRemember = $request->getCheck( 'wpRemember' );
 52+
 53+ if( $wgEnableEmail ) {
 54+ $this->mEmail = $request->getText( 'wpEmail' );
 55+ } else {
 56+ $this->mEmail = '';
 57+ }
 58+ if( !in_array( 'realname', $wgHiddenPrefs ) ) {
 59+ $this->mRealName = $request->getText( 'wpRealName' );
 60+ } else {
 61+ $this->mRealName = '';
 62+ }
 63+
 64+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 65+ $this->mDomain = 'invaliddomain';
 66+ }
 67+ $wgAuth->setDomain( $this->mDomain );
 68+
 69+ # Attempt to generate the User
 70+ $this->mUser = User::newFromName( $this->mName );
 71+ }
 72+
 73+ /**
 74+ * Actually add a user to the database.
 75+ * Give it a User object that has been initialised with a name.
 76+ *
 77+ * @param $u User object.
 78+ * @param $autocreate boolean -- true if this is an autocreation via auth plugin
 79+ * @return User object.
 80+ */
 81+ public function initUser( $autocreate ) {
 82+ global $wgAuth;
 83+
 84+ $this->mUser->addToDatabase();
 85+
 86+ if ( $wgAuth->allowPasswordChange() ) {
 87+ $this->mUser->setPassword( $this->mPassword );
 88+ }
 89+
 90+ $this->mUser->setEmail( $this->mEmail );
 91+ $this->mUser->setRealName( $this->mRealName );
 92+ $this->mUser->setToken();
 93+
 94+ $wgAuth->initUser( $this->mUser, $autocreate );
 95+
 96+ if( $this->mExtUser ) {
 97+ $this->mExtUser->link( $this->mUser->getId() );
 98+ $email = $this->mExtUser->getPref( 'emailaddress' );
 99+ if( $email && !$this->mEmail ) {
 100+ $this->mUser->setEmail( $email );
 101+ }
 102+ }
 103+
 104+ $this->mUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
 105+ $this->mUser->saveSettings();
 106+
 107+ # Update user count
 108+ $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
 109+ $ssUpdate->doUpdate();
 110+
 111+ return $this->mUser;
 112+ }
 113+
 114+ public function attemptLogin(){
 115+ global $wgUser;
 116+ $code = $this->authenticateUserData();
 117+ if( !$code == self::SUCCESS ){
 118+ return $code;
 119+ }
 120+ if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
 121+ $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
 122+ $wgUser->saveSettings();
 123+ } else {
 124+ $wgUser->invalidateCache();
 125+ }
 126+ $wgUser->setCookies();
 127+
 128+ # Reset the throttle
 129+ $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
 130+ global $wgMemc;
 131+ $wgMemc->delete( $key );
 132+
 133+ $injected_html = '';
 134+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
 135+
 136+ return self::SUCCESS;
 137+ }
 138+
 139+ /**
 140+ * Internally authenticate the login request.
 141+ *
 142+ * This may create a local account as a side effect if the
 143+ * authentication plugin allows transparent local account
 144+ * creation.
 145+ */
 146+ public function authenticateUserData() {
 147+ global $wgUser, $wgAuth;
 148+ if ( '' == $this->mName ) {
 149+ return self::NO_NAME;
 150+ }
 151+
 152+ global $wgPasswordAttemptThrottle;
 153+
 154+ $throttleCount = 0;
 155+ if ( is_array( $wgPasswordAttemptThrottle ) ) {
 156+ $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
 157+ $count = $wgPasswordAttemptThrottle['count'];
 158+ $period = $wgPasswordAttemptThrottle['seconds'];
 159+
 160+ global $wgMemc;
 161+ $throttleCount = $wgMemc->get( $throttleKey );
 162+ if ( !$throttleCount ) {
 163+ $wgMemc->add( $throttleKey, 1, $period ); // start counter
 164+ } else if ( $throttleCount < $count ) {
 165+ $wgMemc->incr($throttleKey);
 166+ } else if ( $throttleCount >= $count ) {
 167+ return self::THROTTLED;
 168+ }
 169+ }
 170+
 171+ # Load $wgUser now, and check to see if we're logging in as the same
 172+ # name. This is necessary because loading $wgUser (say by calling
 173+ # getName()) calls the UserLoadFromSession hook, which potentially
 174+ # creates the user in the database. Until we load $wgUser, checking
 175+ # for user existence using User::newFromName($name)->getId() below
 176+ # will effectively be using stale data.
 177+ if ( $wgUser->getName() === $this->mName ) {
 178+ wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
 179+ return self::SUCCESS;
 180+ }
 181+
 182+ $this->mExtUser = ExternalUser::newFromName( $this->mName );
 183+
 184+ # TODO: Allow some magic here for invalid external names, e.g., let the
 185+ # user choose a different wiki name.
 186+ if( is_null( $this->mUser ) || !User::isUsableName( $this->mUser->getName() ) ) {
 187+ return self::ILLEGAL;
 188+ }
 189+
 190+ $isAutoCreated = false;
 191+ if ( 0 == $this->mUser->getID() ) {
 192+ $status = $this->attemptAutoCreate( $this->mUser );
 193+ if ( $status !== self::SUCCESS ) {
 194+ return $status;
 195+ } else {
 196+ $isAutoCreated = true;
 197+ }
 198+ } else {
 199+ $this->mUser->load();
 200+ }
 201+
 202+ # Give general extensions, such as a captcha, a chance to abort logins
 203+ $abort = self::ABORTED;
 204+ if( !wfRunHooks( 'AbortLogin', array( $this->mUser, $this->mPassword, &$abort ) ) ) {
 205+ return $abort;
 206+ }
 207+
 208+ if( !$this->mUser->checkPassword( $this->mPassword ) ) {
 209+ if( $this->mUser->checkTemporaryPassword( $this->mPassword ) ) {
 210+ # The e-mailed temporary password should not be used for actual
 211+ # logins; that's a very sloppy habit, and insecure if an
 212+ # attacker has a few seconds to click "search" on someone's
 213+ # open mail reader.
 214+ #
 215+ # Allow it to be used only to reset the password a single time
 216+ # to a new value, which won't be in the user's e-mail archives
 217+ #
 218+ # For backwards compatibility, we'll still recognize it at the
 219+ # login form to minimize surprises for people who have been
 220+ # logging in with a temporary password for some time.
 221+ #
 222+ # As a side-effect, we can authenticate the user's e-mail ad-
 223+ # dress if it's not already done, since the temporary password
 224+ # was sent via e-mail.
 225+ if( !$this->mUser->isEmailConfirmed() ) {
 226+ $this->mUser->confirmEmail();
 227+ $this->mUser->saveSettings();
 228+ }
 229+
 230+ # At this point we just return an appropriate code/ indicating
 231+ # that the UI should show a password reset form; bot interfaces
 232+ # etc will probably just fail cleanly here.
 233+ $retval = self::RESET_PASS;
 234+ } else {
 235+ $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
 236+ }
 237+ } else {
 238+ $wgAuth->updateUser( $this->mUser );
 239+ $wgUser = $this->mUser;
 240+
 241+ # Reset throttle after a successful login
 242+ if( $throttleCount ) {
 243+ $wgMemc->delete( $throttleKey );
 244+ }
 245+
 246+ if( $isAutoCreated ) {
 247+ # Must be run after $wgUser is set, for correct new user log
 248+ wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
 249+ }
 250+
 251+ $retval = self::SUCCESS;
 252+ }
 253+ wfRunHooks( 'LoginAuthenticateAudit', array( $this->mUser, $this->mPassword, $retval ) );
 254+ return $retval;
 255+ }
 256+
 257+ /**
 258+ * Attempt to automatically create a user on login. Only succeeds if there
 259+ * is an external authentication method which allows it.
 260+ * @return integer Status code
 261+ */
 262+ public function attemptAutoCreate( $user ) {
 263+ global $wgAuth, $wgUser, $wgAutocreatePolicy;
 264+
 265+ if( $wgUser->isBlockedFromCreateAccount() ) {
 266+ wfDebug( __METHOD__.": user is blocked from account creation\n" );
 267+ return self::CREATE_BLOCKED;
 268+ }
 269+
 270+ # If the external authentication plugin allows it, automatically cre-
 271+ # ate a new account for users that are externally defined but have not
 272+ # yet logged in.
 273+ if( $this->mExtUser ) {
 274+ # mExtUser is neither null nor false, so use the new ExternalAuth
 275+ # system.
 276+ if( $wgAutocreatePolicy == 'never' ) {
 277+ return self::NOT_EXISTS;
 278+ }
 279+ if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
 280+ return self::WRONG_PLUGIN_PASS;
 281+ }
 282+ } else {
 283+ # Old AuthPlugin.
 284+ if( !$wgAuth->autoCreate() ) {
 285+ return self::NOT_EXISTS;
 286+ }
 287+ if( !$wgAuth->userExists( $user->getName() ) ) {
 288+ wfDebug( __METHOD__.": user does not exist\n" );
 289+ return self::NOT_EXISTS;
 290+ }
 291+ if( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
 292+ wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
 293+ return self::WRONG_PLUGIN_PASS;
 294+ }
 295+ }
 296+
 297+ wfDebug( __METHOD__.": creating account\n" );
 298+ $this->initUser( true );
 299+ return self::SUCCESS;
 300+ }
 301+
 302+ /**
 303+ * Email the user a new password, if appropriate to do so.
 304+ * @param $text String message key
 305+ * @param $title String message key
 306+ * @return Status code
 307+ */
 308+ public function mailPassword( $text='passwordremindertext', $title='passwordremindertitle' ) {
 309+ global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry;
 310+
 311+ if( wfReadOnly() )
 312+ return self::MAIL_READ_ONLY;
 313+
 314+ if( !$wgAuth->allowPasswordChange() )
 315+ return self::MAIL_PASSCHANGE_FORBIDDEN;
 316+
 317+ # Check against blocked IPs
 318+ # FIXME: -- should we not?
 319+ if( $wgUser->isBlocked() )
 320+ return self::MAIL_BLOCKED;
 321+
 322+ # Check for hooks
 323+ $error = null;
 324+ if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) )
 325+ return $error;
 326+
 327+ # Check against the rate limiter
 328+ if( $wgUser->pingLimiter( 'mailpassword' ) )
 329+ return self::MAIL_PING_THROTTLED;
 330+
 331+ # Check for a valid name
 332+ if ( '' == $this->mName )
 333+ return self::NO_NAME;
 334+ $this->mUser = User::newFromName( $this->mName );
 335+ if( is_null( $this->mUser ) )
 336+ return self::NO_NAME;
 337+
 338+ # And that the resulting user actually exists
 339+ if ( 0 == $this->mUser->getId() )
 340+ return self::NOT_EXISTS;
 341+
 342+ # Check against password throttle
 343+ if ( $this->mUser->isPasswordReminderThrottled() )
 344+ return self::MAIL_PASS_THROTTLED;
 345+
 346+ # User doesn't have email address set
 347+ if ( '' == $this->mUser->getEmail() )
 348+ return self::MAIL_EMPTY_EMAIL;
 349+
 350+ # Don't send to people who are acting fishily by hiding their IP
 351+ $ip = wfGetIP();
 352+ if( !$ip )
 353+ return self::MAIL_BAD_IP;
 354+
 355+ # Let hooks do things with the data
 356+ wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$this->mUser) );
 357+
 358+ $newpass = $this->mUser->randomPassword();
 359+ $this->mUser->setNewpassword( $newpass, true );
 360+ $this->mUser->saveSettings();
 361+
 362+ $message = wfMsgExt( $text, array( 'parsemag' ), $ip, $this->mUser->getName(), $newpass,
 363+ $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
 364+ $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message );
 365+
 366+ if( WikiError::isError( $this->mMailResult ) ) {
 367+ return self::MAIL_ERROR;
 368+ } else {
 369+ return self::SUCCESS;
 370+ }
 371+ }
 372+}
 373+
 374+/**
 375+ * For backwards compatibility, mainly with the state constants, which
 376+ * could be referred to in old extensions with the old class name.
 377+ * @deprecated
 378+ */
 379+class LoginForm extends Login {}
\ No newline at end of file
Property changes on: branches/happy-melon/phase3/includes/Login.php
___________________________________________________________________
Name: svn:eol-style
1380 + native
Index: branches/happy-melon/phase3/includes/specials/SpecialUserlogin.php
@@ -1,90 +1,108 @@
22 <?php
33 /**
4 - * @file
 4+ * SpecialPage for logging users into the wiki
55 * @ingroup SpecialPage
66 */
77
8 -/**
9 - * constructor
10 - */
11 -function wfSpecialUserlogin( $par = '' ) {
12 - global $wgRequest;
13 - if( session_id() == '' ) {
14 - wfSetupSession();
15 - }
 8+class SpecialUserLogin extends SpecialPage {
169
17 - $form = new LoginForm( $wgRequest, $par );
18 - $form->execute();
19 -}
 10+ var $mUsername, $mPassword, $mReturnTo, $mCookieCheck, $mPosted;
 11+ var $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
 12+ var $mRemember, $mDomain, $mLanguage;
 13+ var $mSkipCookieCheck, $mReturnToQuery;
2014
21 -/**
22 - * implements Special:Login
23 - * @ingroup SpecialPage
24 - */
25 -class LoginForm {
 15+ public $mDomains = array();
2616
27 - const SUCCESS = 0;
28 - const NO_NAME = 1;
29 - const ILLEGAL = 2;
30 - const WRONG_PLUGIN_PASS = 3;
31 - const NOT_EXISTS = 4;
32 - const WRONG_PASS = 5;
33 - const EMPTY_PASS = 6;
34 - const RESET_PASS = 7;
35 - const ABORTED = 8;
36 - const CREATE_BLOCKED = 9;
37 - const THROTTLED = 10;
 17+ public $mFormHeader = ''; # Can be filled by hooks etc
 18+ public $mFormFields = array(
 19+ 'Name' => array(
 20+ 'type' => 'text',
 21+ 'label-message' => 'yourname',
 22+ 'id' => 'wpName1',
 23+ 'tabindex' => '1',
 24+ 'size' => '20',
 25+ 'required' => '1',
 26+ ),
 27+ 'Password' => array(
 28+ 'type' => 'password',
 29+ 'label-message' => 'yourpassword',
 30+ 'size' => '20',
 31+ 'id' => 'wpPassword1',
 32+ ),
 33+ 'Domain' => array(
 34+ 'type' => 'select',
 35+ 'id' => 'wpDomain',
 36+ 'label-message' => 'yourdomainname',
 37+ 'options' => null,
 38+ 'default' => null,
 39+ ),
 40+ 'Remember' => array(
 41+ 'type' => 'check',
 42+ 'label-message' => 'remembermypassword',
 43+ 'id' => 'wpRemember',
 44+ )
 45+ );
3846
39 - var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
40 - var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
41 - var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
42 - var $mSkipCookieCheck, $mReturnToQuery;
 47+ protected $mLogin; # Login object
4348
44 - private $mExtUser = null;
 49+ public function __construct(){
 50+ parent::__construct( 'Userlogin' );
 51+ }
4552
 53+ function execute( $par ) {
 54+ global $wgRequest;
 55+ $this->loadQuery();
 56+ $this->mLogin = new Login();
 57+
 58+ # Redirect out for account creation, for B/C
 59+ $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' );
 60+ if( $type == 'signup' ){
 61+ $sp = new SpecialCreateAccount();
 62+ $sp->execute( $par );
 63+ return;
 64+ }
 65+
 66+ if ( !is_null( $this->mCookieCheck ) ) {
 67+ $this->onCookieRedirectCheck();
 68+ return;
 69+ } else if( $this->mPosted ) {
 70+ if ( $this->mMailmypassword ) {
 71+ return $this->showMailPage();
 72+ } else {
 73+ return $this->processLogin();
 74+ }
 75+ } else {
 76+ $this->mainLoginForm( '' );
 77+ }
 78+ }
 79+
4680 /**
47 - * Constructor
48 - * @param WebRequest $request A WebRequest object passed by reference
 81+ * Load member variables from the HTTP request data
 82+ * @param $par String the fragment passed to execute()
4983 */
50 - function LoginForm( &$request, $par = '' ) {
51 - global $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 84+ protected function loadQuery(){
 85+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
5286
53 - $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]]
54 - $this->mName = $request->getText( 'wpName' );
55 - $this->mPassword = $request->getText( 'wpPassword' );
56 - $this->mRetype = $request->getText( 'wpRetype' );
57 - $this->mDomain = $request->getText( 'wpDomain' );
58 - $this->mReturnTo = $request->getVal( 'returnto' );
59 - $this->mReturnToQuery = $request->getVal( 'returntoquery' );
60 - $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
61 - $this->mPosted = $request->wasPosted();
62 - $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' );
63 - $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
64 - && $wgEnableEmail;
65 - $this->mMailmypassword = $request->getCheck( 'wpMailmypassword' )
 87+ $this->mUsername = $wgRequest->getText( 'wpName' );
 88+ $this->mPassword = $wgRequest->getText( 'wpPassword' );
 89+ $this->mDomain = $wgRequest->getText( 'wpDomain' );
 90+ $this->mLanguage = $wgRequest->getText( 'uselang' );
 91+
 92+ $this->mReturnTo = $wgRequest->getVal( 'returnto' );
 93+ $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 94+ $this->mCookieCheck = $wgRequest->getVal( 'wpCookieCheck' );
 95+
 96+ $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' )
6697 && $wgEnableEmail;
67 - $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
68 - $this->mAction = $request->getVal( 'action' );
69 - $this->mRemember = $request->getCheck( 'wpRemember' );
70 - $this->mLanguage = $request->getText( 'uselang' );
71 - $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
 98+ $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
 99+ $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' );
 100+ $this->mPosted = $wgRequest->wasPosted();
72101
73102 if ( $wgRedirectOnLogin ) {
74103 $this->mReturnTo = $wgRedirectOnLogin;
75104 $this->mReturnToQuery = '';
76105 }
77106
78 - if( $wgEnableEmail ) {
79 - $this->mEmail = $request->getText( 'wpEmail' );
80 - } else {
81 - $this->mEmail = '';
82 - }
83 - if( !in_array( 'realname', $wgHiddenPrefs ) ) {
84 - $this->mRealName = $request->getText( 'wpRealName' );
85 - } else {
86 - $this->mRealName = '';
87 - }
88 -
89107 if( !$wgAuth->validDomain( $this->mDomain ) ) {
90108 $this->mDomain = 'invaliddomain';
91109 }
@@ -98,519 +116,349 @@
99117 }
100118 }
101119
102 - function execute() {
103 - if ( !is_null( $this->mCookieCheck ) ) {
104 - $this->onCookieRedirectCheck( $this->mCookieCheck );
105 - return;
106 - } else if( $this->mPosted ) {
107 - if( $this->mCreateaccount ) {
108 - return $this->addNewAccount();
109 - } else if ( $this->mCreateaccountMail ) {
110 - return $this->addNewAccountMailPassword();
111 - } else if ( $this->mMailmypassword ) {
112 - return $this->mailPassword();
113 - } else if ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
114 - return $this->processLogin();
115 - }
116 - }
117 - $this->mainLoginForm( '' );
118 - }
119 -
120120 /**
121 - * @private
 121+ * Show the main login form
 122+ * @param $msg String a message key for a warning/error message
 123+ * that may have been generated on a previous iteration
122124 */
123 - function addNewAccountMailPassword() {
124 - global $wgOut;
 125+ protected function mainLoginForm( $msg, $msgtype = 'error' ) {
 126+ global $wgUser, $wgOut, $wgEnableEmail;
 127+ global $wgCookiePrefix, $wgLoginLanguageSelector;
 128+ global $wgAuth, $wgCookieExpiration;
125129
126 - if ('' == $this->mEmail) {
127 - $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
128 - return;
 130+ # Preload the name field with something if we can
 131+ if ( '' == $this->mUsername ) {
 132+ if ( $wgUser->isLoggedIn() ) {
 133+ $this->mUsername = $wgUser->getName();
 134+ } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) {
 135+ $this->mUsername = $_COOKIE[$wgCookiePrefix.'UserName'];
 136+ }
129137 }
130 -
131 - $u = $this->addNewaccountInternal();
132 -
133 - if ($u == NULL) {
134 - return;
135 - }
136 -
137 - // Wipe the initial password and mail a temporary one
138 - $u->setPassword( null );
139 - $u->saveSettings();
140 - $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
141 -
142 - wfRunHooks( 'AddNewAccount', array( $u, true ) );
143 - $u->addNewUserLogEntry();
144 -
145 - $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
146 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
147 - $wgOut->setArticleRelated( false );
148 -
149 - if( WikiError::isError( $result ) ) {
150 - $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
 138+ if( $this->mUsername ){
 139+ $this->mFormFields['Name']['default'] = $this->mUsername;
 140+ $this->mFormFields['Password']['autofocus'] = '1';
151141 } else {
152 - $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
153 - $wgOut->returnToMain( false );
 142+ $this->mFormFields['Name']['autofocus'] = '1';
154143 }
155 - $u = 0;
156 - }
157144
158 -
159 - /**
160 - * @private
161 - */
162 - function addNewAccount() {
163 - global $wgUser, $wgEmailAuthentication;
164 -
165 - # Create the account and abort if there's a problem doing so
166 - $u = $this->addNewAccountInternal();
167 - if( $u == NULL )
168 - return;
169 -
170 - # If we showed up language selection links, and one was in use, be
171 - # smart (and sensible) and save that language as the user's preference
172 - global $wgLoginLanguageSelector;
173 - if( $wgLoginLanguageSelector && $this->mLanguage )
174 - $u->setOption( 'language', $this->mLanguage );
175 -
176 - # Send out an email authentication message if needed
177 - if( $wgEmailAuthentication && User::isValidEmailAddr( $u->getEmail() ) ) {
178 - global $wgOut;
179 - $error = $u->sendConfirmationMail();
180 - if( WikiError::isError( $error ) ) {
181 - $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
182 - } else {
183 - $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 145+ # Parse the error message if we got one
 146+ if( $msg ){
 147+ if( $msgtype == 'error' ){
 148+ $msg = wfMsg( 'loginerror' ) . ' ' . $msg;
184149 }
185 - }
186 -
187 - # Save settings (including confirmation token)
188 - $u->saveSettings();
189 -
190 - # If not logged in, assume the new account as the current one and set
191 - # session cookies then show a "welcome" message or a "need cookies"
192 - # message as needed
193 - if( $wgUser->isAnon() ) {
194 - $wgUser = $u;
195 - $wgUser->setCookies();
196 - wfRunHooks( 'AddNewAccount', array( $wgUser ) );
197 - $wgUser->addNewUserLogEntry();
198 - if( $this->hasSessionCookie() ) {
199 - return $this->successfulCreation();
200 - } else {
201 - return $this->cookieRedirectCheck( 'new' );
202 - }
 150+ $msg = Html::rawElement(
 151+ 'div',
 152+ array( 'class' => $msgtype . 'box' ),
 153+ $msg
 154+ );
203155 } else {
204 - # Confirm that the account was created
205 - global $wgOut;
206 - $self = SpecialPage::getTitleFor( 'Userlogin' );
207 - $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
208 - $wgOut->setArticleRelated( false );
209 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
210 - $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $u->getName() ) );
211 - $wgOut->returnToMain( false, $self );
212 - wfRunHooks( 'AddNewAccount', array( $u ) );
213 - $u->addNewUserLogEntry();
214 - return true;
 156+ $msg = '';
215157 }
216 - }
217158
218 - /**
219 - * @private
220 - */
221 - function addNewAccountInternal() {
222 - global $wgUser, $wgOut;
223 - global $wgEnableSorbs, $wgProxyWhitelist;
224 - global $wgMemc, $wgAccountCreationThrottle;
225 - global $wgAuth, $wgMinimalPasswordLength;
226 - global $wgEmailConfirmToEdit;
227 -
228 - // If the user passes an invalid domain, something is fishy
229 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
230 - $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
231 - return false;
 159+ # Make sure the returnTo strings don't get lost if the
 160+ # user changes language, etc
 161+ $linkq = array();
 162+ if ( !empty( $this->mReturnTo ) ) {
 163+ $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
 164+ if ( !empty( $this->mReturnToQuery ) )
 165+ $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
232166 }
233167
234 - // If we are not allowing users to login locally, we should be checking
235 - // to see if the user is actually able to authenticate to the authenti-
236 - // cation server before they create an account (otherwise, they can
237 - // create a local account and login as any domain user). We only need
238 - // to check this for domains that aren't local.
239 - if( 'local' != $this->mDomain && '' != $this->mDomain ) {
240 - if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
241 - $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
242 - return false;
243 - }
244 - }
 168+ # Pass any language selection on to the mode switch link
 169+ if( $wgLoginLanguageSelector && $this->mLanguage )
 170+ $linkq['uselang'] = $this->mLanguage;
245171
246 - if ( wfReadOnly() ) {
247 - $wgOut->readOnlyPage();
248 - return false;
249 - }
 172+ $skin = $wgUser->getSkin();
 173+ $link = $skin->link(
 174+ SpecialPage::getTitleFor( 'CreateAccount' ),
 175+ wfMsgHtml( 'nologinlink' ),
 176+ array(),
 177+ $linkq );
250178
251 - # Check permissions
252 - if ( !$wgUser->isAllowed( 'createaccount' ) ) {
253 - $this->userNotPrivilegedMessage();
254 - return false;
255 - } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
256 - $this->userBlockedMessage();
257 - return false;
258 - }
 179+ # Don't show a "create account" link if the user can't
 180+ $link = $wgUser->isAllowed( 'createaccount' ) && !$wgUser->isLoggedIn()
 181+ ? wfMsgWikiHtml( 'nologin', $link )
 182+ : '';
259183
260 - $ip = wfGetIP();
261 - if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
262 - $wgUser->inSorbsBlacklist( $ip ) )
263 - {
264 - $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
265 - return;
 184+ # Prepare language selection links as needed
 185+ $langSelector = $wgLoginLanguageSelector
 186+ ? Html::rawElement(
 187+ 'div',
 188+ array( 'id' => 'languagelinks' ),
 189+ self::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
 190+ : '';
 191+
 192+ # Add a 'mail reset' button if available
 193+ $buttons = '';
 194+ if( $wgEnableEmail && $wgAuth->allowPasswordChange() ){
 195+ $buttons = Html::element(
 196+ 'input',
 197+ array(
 198+ 'type' => 'submit',
 199+ 'name' => 'wpMailmypassword',
 200+ 'value' => wfMsg( 'mailmypassword' ),
 201+ 'id' => 'wpMailmypassword',
 202+ )
 203+ );
266204 }
267205
268 - # Now create a dummy user ($u) and check if it is valid
269 - $name = trim( $this->mName );
270 - $u = User::newFromName( $name, 'creatable' );
271 - if ( is_null( $u ) ) {
272 - $this->mainLoginForm( wfMsg( 'noname' ) );
273 - return false;
 206+ # Give authentication and captcha plugins a chance to
 207+ # modify the form, by hook or by using $wgAuth
 208+ $wgAuth->modifyUITemplate( $this, 'login' );
 209+ wfRunHooks( 'UserLoginForm', array( &$this ) );
 210+
 211+ # The most likely use of the hook is to enable domains;
 212+ # check that now, and add fields if necessary
 213+ if( $this->mDomains ){
 214+ $this->mFormFields['Domain']['options'] = $this->mDomains;
 215+ $this->mFormFields['Domain']['default'] = $this->mDomain;
 216+ } else {
 217+ unset( $this->mFormFields['Domain'] );
274218 }
275 -
276 - if ( 0 != $u->idForName() ) {
277 - $this->mainLoginForm( wfMsg( 'userexists' ) );
278 - return false;
 219+
 220+ # Or to tweak the 'remember my password' checkbox
 221+ if( !($wgCookieExpiration > 0) ){
 222+ # Remove it altogether
 223+ unset( $this->mFormFields['Remember'] );
 224+ } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
 225+ # Or check it by default
 226+ # FIXME: this doesn't always work?
 227+ $this->mFormFields['Remember']['checked'] = '1';
279228 }
 229+
 230+ $form = new HTMLForm( $this->mFormFields, '' );
 231+ $form->setTitle( $this->getTitle() );
 232+ $form->setSubmitText( wfMsg( 'login' ) );
 233+ $form->setSubmitId( 'wpLoginAttempt' );
 234+ $form->suppressReset();
 235+ $form->loadData();
 236+
 237+ $formContents = ''
 238+ . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
 239+ $link )
 240+ . Html::rawElement( 'div', array( 'id' => 'userloginprompt' ),
 241+ wfMsgExt( 'loginprompt', array( 'parseinline' ) ) )
 242+ . $this->mFormHeader
 243+ . $langSelector
 244+ . $form->getBody()
 245+ . $form->getButtons()
 246+ . $buttons
 247+ . Xml::hidden( 'returnto', $this->mReturnTo )
 248+ . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
 249+ ;
280250
281 - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
282 - $this->mainLoginForm( wfMsg( 'badretype' ) );
283 - return false;
284 - }
 251+ $wgOut->setPageTitle( wfMsg( 'login' ) );
 252+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 253+ $wgOut->setArticleRelated( false );
 254+ $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
285255
286 - # check for minimal password length
287 - $valid = $u->isValidPassword( $this->mPassword );
288 - if ( $valid !== true ) {
289 - if ( !$this->mCreateaccountMail ) {
290 - $this->mainLoginForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
291 - return false;
292 - } else {
293 - # do not force a password for account creation by email
294 - # set invalid password, it will be replaced later by a random generated password
295 - $this->mPassword = null;
296 - }
297 - }
 256+ $wgOut->addHTML(
 257+ Html::rawElement(
 258+ 'div',
 259+ array( 'id' => 'loginstart' ),
 260+ wfMsgExt( 'loginstart', array( 'parseinline' ) )
 261+ ) .
 262+ $msg .
 263+ Html::rawElement(
 264+ 'div',
 265+ array( 'id' => 'userloginForm' ),
 266+ $form->wrapForm( $formContents )
 267+ ) .
 268+ Html::rawElement(
 269+ 'div',
 270+ array( 'id' => 'loginend' ),
 271+ wfMsgExt( 'loginend', array( 'parseinline' ) )
 272+ )
 273+ );
298274
299 - # if you need a confirmed email address to edit, then obviously you
300 - # need an email address.
301 - if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
302 - $this->mainLoginForm( wfMsg( 'noemailtitle' ) );
303 - return false;
304 - }
 275+ }
305276
306 - if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
307 - $this->mainLoginForm( wfMsg( 'invalidemailaddress' ) );
308 - return false;
309 - }
310 -
311 - # Set some additional data so the AbortNewAccount hook can be used for
312 - # more than just username validation
313 - $u->setEmail( $this->mEmail );
314 - $u->setRealName( $this->mRealName );
315 -
316 - $abortError = '';
317 - if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
318 - // Hook point to add extra creation throttles and blocks
319 - wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
320 - $this->mainLoginForm( $abortError );
321 - return false;
322 - }
323 -
324 - if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
325 - $key = wfMemcKey( 'acctcreate', 'ip', $ip );
326 - $value = $wgMemc->get( $key );
327 - if ( !$value ) {
328 - $wgMemc->set( $key, 0, 86400 );
329 - }
330 - if ( $value >= $wgAccountCreationThrottle ) {
331 - $this->throttleHit( $wgAccountCreationThrottle );
332 - return false;
333 - }
334 - $wgMemc->incr( $key );
335 - }
336 -
337 - if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
338 - $this->mainLoginForm( wfMsg( 'externaldberror' ) );
339 - return false;
340 - }
341 -
342 - return $this->initUser( $u, false );
343 - }
344 -
345277 /**
346 - * Actually add a user to the database.
347 - * Give it a User object that has been initialised with a name.
 278+ * Check if a session cookie is present.
348279 *
349 - * @param $u User object.
350 - * @param $autocreate boolean -- true if this is an autocreation via auth plugin
351 - * @return User object.
 280+ * This will not pick up a cookie set during _this_ request, but is meant
 281+ * to ensure that the client is returning the cookie which was set on a
 282+ * previous pass through the system.
 283+ *
352284 * @private
353285 */
354 - function initUser( $u, $autocreate ) {
355 - global $wgAuth;
 286+ protected function hasSessionCookie() {
 287+ global $wgDisableCookieCheck, $wgRequest;
 288+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
 289+ }
356290
357 - $u->addToDatabase();
 291+ /**
 292+ * Do a redirect back to the same page, so we can check any
 293+ * new session cookies.
 294+ */
 295+ protected function cookieRedirectCheck() {
 296+ global $wgOut;
358297
359 - if ( $wgAuth->allowPasswordChange() ) {
360 - $u->setPassword( $this->mPassword );
361 - }
 298+ $query = array( 'wpCookieCheck' => '1');
 299+ if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
 300+ $check = $this->getTitle()->getFullURL( $query );
362301
363 - $u->setEmail( $this->mEmail );
364 - $u->setRealName( $this->mRealName );
365 - $u->setToken();
 302+ return $wgOut->redirect( $check );
 303+ }
366304
367 - $wgAuth->initUser( $u, $autocreate );
368 -
369 - if ( $this->mExtUser ) {
370 - $this->mExtUser->link( $u->getId() );
371 - $email = $this->mExtUser->getPref( 'emailaddress' );
372 - if ( $email && !$this->mEmail ) {
373 - $u->setEmail( $email );
374 - }
 305+ /**
 306+ * Check the cookies and show errors if they're not enabled.
 307+ * @param $type String action being performed
 308+ */
 309+ protected function onCookieRedirectCheck() {
 310+ if ( !$this->hasSessionCookie() ) {
 311+ return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
 312+ } else {
 313+ return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
375314 }
376 -
377 - $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
378 - $u->saveSettings();
379 -
380 - # Update user count
381 - $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
382 - $ssUpdate->doUpdate();
383 -
384 - return $u;
385315 }
386316
387317 /**
388 - * Internally authenticate the login request.
389 - *
390 - * This may create a local account as a side effect if the
391 - * authentication plugin allows transparent local account
392 - * creation.
393 - *
394 - * @public
 318+ * Produce a bar of links which allow the user to select another language
 319+ * during login/registration but retain "returnto"
 320+ * @param $title Title to use in the link
 321+ * @param $returnTo query string to append
 322+ * @return String HTML for bar
395323 */
396 - function authenticateUserData() {
397 - global $wgUser, $wgAuth;
398 - if ( '' == $this->mName ) {
399 - return self::NO_NAME;
400 - }
401 -
402 - global $wgPasswordAttemptThrottle;
 324+ public static function makeLanguageSelector( $title, $returnTo=false ) {
 325+ global $wgLang;
403326
404 - $throttleCount = 0;
405 - if ( is_array( $wgPasswordAttemptThrottle ) ) {
406 - $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
407 - $count = $wgPasswordAttemptThrottle['count'];
408 - $period = $wgPasswordAttemptThrottle['seconds'];
409 -
410 - global $wgMemc;
411 - $throttleCount = $wgMemc->get( $throttleKey );
412 - if ( !$throttleCount ) {
413 - $wgMemc->add( $throttleKey, 1, $period ); // start counter
414 - } else if ( $throttleCount < $count ) {
415 - $wgMemc->incr($throttleKey);
416 - } else if ( $throttleCount >= $count ) {
417 - return self::THROTTLED;
 327+ $msg = wfMsgForContent( 'loginlanguagelinks' );
 328+ if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
 329+ $langs = explode( "\n", $msg );
 330+ $links = array();
 331+ foreach( $langs as $lang ) {
 332+ $lang = trim( $lang, '* ' );
 333+ $parts = explode( '|', $lang );
 334+ if (count($parts) >= 2) {
 335+ $links[] = SpecialUserLogin::makeLanguageSelectorLink(
 336+ $parts[0], $parts[1], $title, $returnTo );
 337+ }
418338 }
 339+ return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
 340+ } else {
 341+ return '';
419342 }
 343+ }
420344
421 - // Load $wgUser now, and check to see if we're logging in as the same
422 - // name. This is necessary because loading $wgUser (say by calling
423 - // getName()) calls the UserLoadFromSession hook, which potentially
424 - // creates the user in the database. Until we load $wgUser, checking
425 - // for user existence using User::newFromName($name)->getId() below
426 - // will effectively be using stale data.
427 - if ( $wgUser->getName() === $this->mName ) {
428 - wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
429 - return self::SUCCESS;
430 - }
 345+ /**
 346+ * Create a language selector link for a particular language
 347+ * Links back to this page preserving type and returnto
 348+ * @param $text Link text
 349+ * @param $lang Language code
 350+ * @param $title Title to link to
 351+ * @param $returnTo String returnto query
 352+ */
 353+ public static function makeLanguageSelectorLink( $text, $lang, $title, $returnTo=false ) {
 354+ global $wgUser;
 355+ $attr = array( 'uselang' => $lang );
 356+ if( $returnTo )
 357+ $attr['returnto'] = $returnTo;
 358+ $skin = $wgUser->getSkin();
 359+ return $skin->linkKnown(
 360+ $title,
 361+ htmlspecialchars( $text ),
 362+ array(),
 363+ $attr
 364+ );
 365+ }
431366
432 - $this->mExtUser = ExternalUser::newFromName( $this->mName );
 367+ /**
 368+ * Display a "login successful" page.
 369+ * @param $msgname String message key to display
 370+ * @param $html String HTML to optionally add
 371+ * @param $returnto Title to returnto
 372+ * @param $returntoQuery String query string for returnto link
 373+ */
 374+ public static function displaySuccessfulLogin( $msgname, $injected_html='', $returnto=false, $returntoQuery=false ) {
 375+ global $wgOut, $wgUser;
433376
434 - # TODO: Allow some magic here for invalid external names, e.g., let the
435 - # user choose a different wiki name.
436 - $u = User::newFromName( $this->mName );
437 - if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
438 - return self::ILLEGAL;
439 - }
 377+ $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
 378+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 379+ $wgOut->setArticleRelated( false );
 380+ $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
 381+ $wgOut->addHTML( $injected_html );
440382
441 - $isAutoCreated = false;
442 - if ( 0 == $u->getID() ) {
443 - $status = $this->attemptAutoCreate( $u );
444 - if ( $status !== self::SUCCESS ) {
445 - return $status;
446 - } else {
447 - $isAutoCreated = true;
448 - }
 383+ if ( $returnto ) {
 384+ $wgOut->returnToMain( null, $returnto, $this->mReturnToQuery );
449385 } else {
450 - $u->load();
 386+ $wgOut->returnToMain( null );
451387 }
452 -
453 - // Give general extensions, such as a captcha, a chance to abort logins
454 - $abort = self::ABORTED;
455 - if( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort ) ) ) {
456 - return $abort;
457 - }
458 -
459 - if (!$u->checkPassword( $this->mPassword )) {
460 - if( $u->checkTemporaryPassword( $this->mPassword ) ) {
461 - // The e-mailed temporary password should not be used for actu-
462 - // al logins; that's a very sloppy habit, and insecure if an
463 - // attacker has a few seconds to click "search" on someone's o-
464 - // pen mail reader.
465 - //
466 - // Allow it to be used only to reset the password a single time
467 - // to a new value, which won't be in the user's e-mail ar-
468 - // chives.
469 - //
470 - // For backwards compatibility, we'll still recognize it at the
471 - // login form to minimize surprises for people who have been
472 - // logging in with a temporary password for some time.
473 - //
474 - // As a side-effect, we can authenticate the user's e-mail ad-
475 - // dress if it's not already done, since the temporary password
476 - // was sent via e-mail.
477 - if( !$u->isEmailConfirmed() ) {
478 - $u->confirmEmail();
479 - $u->saveSettings();
480 - }
481 -
482 - // At this point we just return an appropriate code/ indicating
483 - // that the UI should show a password reset form; bot inter-
484 - // faces etc will probably just fail cleanly here.
485 - $retval = self::RESET_PASS;
486 - } else {
487 - $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
488 - }
489 - } else {
490 - $wgAuth->updateUser( $u );
491 - $wgUser = $u;
492 -
493 - // Please reset throttle for successful logins, thanks!
494 - if($throttleCount) {
495 - $wgMemc->delete($throttleKey);
496 - }
497 -
498 - if ( $isAutoCreated ) {
499 - // Must be run after $wgUser is set, for correct new user log
500 - wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
501 - }
502 -
503 - $retval = self::SUCCESS;
504 - }
505 - wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
506 - return $retval;
507388 }
508389
509390 /**
510 - * Attempt to automatically create a user on login. Only succeeds if there
511 - * is an external authentication method which allows it.
512 - * @return integer Status code
 391+ * Run any hooks registered for logins, then HTTP redirect to
 392+ * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
 393+ * nice message here, but that's really not as useful as just being sent to
 394+ * wherever you logged in from. It should be clear that the action was
 395+ * successful, given the lack of error messages plus the appearance of your
 396+ * name in the upper right.
513397 */
514 - function attemptAutoCreate( $user ) {
515 - global $wgAuth, $wgUser, $wgAutocreatePolicy;
 398+ public static function successfulLogin( $message, $returnTo='', $returnToQuery='' ) {
 399+ global $wgUser, $wgOut;
516400
517 - if ( $wgUser->isBlockedFromCreateAccount() ) {
518 - wfDebug( __METHOD__.": user is blocked from account creation\n" );
519 - return self::CREATE_BLOCKED;
520 - }
 401+ # Run any hooks; display injected HTML if any, else redirect
 402+ $injected_html = '';
 403+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
521404
522 - /**
523 - * If the external authentication plugin allows it, automatically cre-
524 - * ate a new account for users that are externally defined but have not
525 - * yet logged in.
526 - */
527 - if ( $this->mExtUser ) {
528 - # mExtUser is neither null nor false, so use the new ExternalAuth
529 - # system.
530 - if ( $wgAutocreatePolicy == 'never' ) {
531 - return self::NOT_EXISTS;
532 - }
533 - if ( !$this->mExtUser->authenticate( $this->mPassword ) ) {
534 - return self::WRONG_PLUGIN_PASS;
535 - }
 405+ if( $injected_html !== '' ) {
 406+ SpecialUserLogin::displaySuccessfulLogin( $message, $injected_html );
536407 } else {
537 - # Old AuthPlugin.
538 - if ( !$wgAuth->autoCreate() ) {
539 - return self::NOT_EXISTS;
 408+ $titleObj = Title::newFromText( $returnTo );
 409+ if ( !$titleObj instanceof Title ) {
 410+ $titleObj = Title::newMainPage();
540411 }
541 - if ( !$wgAuth->userExists( $user->getName() ) ) {
542 - wfDebug( __METHOD__.": user does not exist\n" );
543 - return self::NOT_EXISTS;
544 - }
545 - if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
546 - wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
547 - return self::WRONG_PLUGIN_PASS;
548 - }
 412+ $wgOut->redirect( $titleObj->getFullURL( $returnToQuery ) );
549413 }
550 -
551 - wfDebug( __METHOD__.": creating account\n" );
552 - $user = $this->initUser( $user, true );
553 - return self::SUCCESS;
554414 }
 415+
555416
556 - function processLogin() {
 417+ protected function processLogin(){
557418 global $wgUser, $wgAuth;
558 -
559 - switch ( $this->authenticateUserData() ) {
560 - case self::SUCCESS:
561 - # We've verified now, update the real record
562 - if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
563 - $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
564 - $wgUser->saveSettings();
565 - } else {
566 - $wgUser->invalidateCache();
567 - }
568 - $wgUser->setCookies();
569 -
570 - // Reset the throttle
571 - $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
572 - global $wgMemc;
573 - $wgMemc->delete( $key );
574 -
 419+ $result = $this->mLogin->attemptLogin();
 420+ switch ( $result ) {
 421+ case Login::SUCCESS:
575422 if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
576 - /* Replace the language object to provide user interface in
577 - * correct language immediately on this first page load.
578 - */
 423+ # Replace the language object to provide user interface in
 424+ # correct language immediately on this first page load.
579425 global $wgLang, $wgRequest;
580426 $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
581427 $wgLang = Language::factory( $code );
582 - return $this->successfulLogin();
 428+ return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
583429 } else {
584 - return $this->cookieRedirectCheck( 'login' );
 430+ # Do a redirect check to ensure that the cookies are
 431+ # being retained by the user's browser.
 432+ return $this->cookieRedirectCheck();
585433 }
586434 break;
587435
588 - case self::NO_NAME:
589 - case self::ILLEGAL:
 436+ case Login::NO_NAME:
 437+ case Login::ILLEGAL:
590438 $this->mainLoginForm( wfMsg( 'noname' ) );
591439 break;
592 - case self::WRONG_PLUGIN_PASS:
 440+ case Login::WRONG_PLUGIN_PASS:
593441 $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
594442 break;
595 - case self::NOT_EXISTS:
 443+ case Login::NOT_EXISTS:
596444 if( $wgUser->isAllowed( 'createaccount' ) ){
597445 $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
598446 } else {
599447 $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
600448 }
601449 break;
602 - case self::WRONG_PASS:
 450+ case Login::WRONG_PASS:
603451 $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
604452 break;
605 - case self::EMPTY_PASS:
 453+ case Login::EMPTY_PASS:
606454 $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
607455 break;
608 - case self::RESET_PASS:
 456+ case Login::RESET_PASS:
609457 $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
610458 break;
611 - case self::CREATE_BLOCKED:
 459+ case Login::CREATE_BLOCKED:
612460 $this->userBlockedMessage();
613461 break;
614 - case self::THROTTLED:
 462+ case Login::THROTTLED:
615463 $this->mainLoginForm( wfMsg( 'login-throttled' ) );
616464 break;
617465 default:
@@ -618,6 +466,11 @@
619467 }
620468 }
621469
 470+ /**
 471+ * 'Shell out' to Special:ResetPass to get the user to
 472+ * set a new permanent password from a temporary one.
 473+ * @param $error String message
 474+ */
622475 function resetLoginForm( $error ) {
623476 global $wgOut;
624477 $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) );
@@ -626,438 +479,67 @@
627480 }
628481
629482 /**
630 - * @private
 483+ * Attempt to send the user a password-reset mail, and display
 484+ * the results (good, bad or ugly).
 485+ * @return unknown_type
631486 */
632 - function mailPassword() {
633 - global $wgUser, $wgOut, $wgAuth;
634 -
635 - if ( wfReadOnly() ) {
636 - $wgOut->readOnlyPage();
637 - return false;
638 - }
639 -
640 - if( !$wgAuth->allowPasswordChange() ) {
641 - $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
642 - return;
643 - }
644 -
645 - # Check against blocked IPs
646 - # fixme -- should we not?
647 - if( $wgUser->isBlocked() ) {
648 - $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
649 - return;
650 - }
651 -
652 - // Check for hooks
653 - $error = null;
654 - if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) ) {
655 - $this->mainLoginForm( $error );
656 - return;
657 - }
658 -
659 - # Check against the rate limiter
660 - if( $wgUser->pingLimiter( 'mailpassword' ) ) {
661 - $wgOut->rateLimited();
662 - return;
663 - }
664 -
665 - if ( '' == $this->mName ) {
666 - $this->mainLoginForm( wfMsg( 'noname' ) );
667 - return;
668 - }
669 - $u = User::newFromName( $this->mName );
670 - if( is_null( $u ) ) {
671 - $this->mainLoginForm( wfMsg( 'noname' ) );
672 - return;
673 - }
674 - if ( 0 == $u->getID() ) {
675 - $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $u->getName() ) ) );
676 - return;
677 - }
678 -
679 - # Check against password throttle
680 - if ( $u->isPasswordReminderThrottled() ) {
681 - global $wgPasswordReminderResendTime;
682 - # Round the time in hours to 3 d.p., in case someone is specifying
683 - # minutes or seconds.
684 - $this->mainLoginForm( wfMsgExt( 'throttled-mailpassword', array( 'parsemag' ),
685 - round( $wgPasswordReminderResendTime, 3 ) ) );
686 - return;
687 - }
688 -
689 - $result = $this->mailPasswordInternal( $u, true, 'passwordremindertitle', 'passwordremindertext' );
690 - if( WikiError::isError( $result ) ) {
691 - $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
692 - } else {
693 - $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' );
694 - }
695 - }
696 -
697 -
698 - /**
699 - * @param object user
700 - * @param bool throttle
701 - * @param string message name of email title
702 - * @param string message name of email text
703 - * @return mixed true on success, WikiError on failure
704 - * @private
705 - */
706 - function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
707 - global $wgServer, $wgScript, $wgUser, $wgNewPasswordExpiry;
708 -
709 - if ( '' == $u->getEmail() ) {
710 - return new WikiError( wfMsg( 'noemail', $u->getName() ) );
711 - }
712 - $ip = wfGetIP();
713 - if( !$ip ) {
714 - return new WikiError( wfMsg( 'badipaddress' ) );
715 - }
716 -
717 - wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$u) );
718 -
719 - $np = $u->randomPassword();
720 - $u->setNewpassword( $np, $throttle );
721 - $u->saveSettings();
722 -
723 - $m = wfMsgExt( $emailText, array( 'parsemag' ), $ip, $u->getName(), $np,
724 - $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
725 - $result = $u->sendMail( wfMsg( $emailTitle ), $m );
726 -
727 - return $result;
728 - }
729 -
730 -
731 - /**
732 - * Run any hooks registered for logins, then HTTP redirect to
733 - * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
734 - * nice message here, but that's really not as useful as just being sent to
735 - * wherever you logged in from. It should be clear that the action was
736 - * successful, given the lack of error messages plus the appearance of your
737 - * name in the upper right.
738 - *
739 - * @private
740 - */
741 - function successfulLogin() {
742 - global $wgUser, $wgOut;
743 -
744 - # Run any hooks; display injected HTML if any, else redirect
745 - $injected_html = '';
746 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
747 -
748 - if( $injected_html !== '' ) {
749 - $this->displaySuccessfulLogin( 'loginsuccess', $injected_html );
750 - } else {
751 - $titleObj = Title::newFromText( $this->mReturnTo );
752 - if ( !$titleObj instanceof Title ) {
753 - $titleObj = Title::newMainPage();
754 - }
755 - $wgOut->redirect( $titleObj->getFullURL( $this->mReturnToQuery ) );
756 - }
757 - }
758 -
759 - /**
760 - * Run any hooks registered for logins, then display a message welcoming
761 - * the user.
762 - *
763 - * @private
764 - */
765 - function successfulCreation() {
766 - global $wgUser, $wgOut;
767 -
768 - # Run any hooks; display injected HTML
769 - $injected_html = '';
770 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
771 -
772 - $this->displaySuccessfulLogin( 'welcomecreation', $injected_html );
773 - }
774 -
775 - /**
776 - * Display a "login successful" page.
777 - */
778 - private function displaySuccessfulLogin( $msgname, $injected_html ) {
779 - global $wgOut, $wgUser;
780 -
781 - $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
782 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
783 - $wgOut->setArticleRelated( false );
784 - $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
785 - $wgOut->addHTML( $injected_html );
786 -
787 - if ( !empty( $this->mReturnTo ) ) {
788 - $wgOut->returnToMain( null, $this->mReturnTo, $this->mReturnToQuery );
789 - } else {
790 - $wgOut->returnToMain( null );
791 - }
792 - }
793 -
794 - /** */
795 - function userNotPrivilegedMessage($errors) {
 487+ protected function showMailPage(){
796488 global $wgOut;
 489+ $result = $this->mLogin->mailPassword();
797490
798 - $wgOut->setPageTitle( wfMsg( 'permissionserrors' ) );
799 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
800 - $wgOut->setArticleRelated( false );
801 -
802 - $wgOut->addWikitext( $wgOut->formatPermissionsErrorMessage( $errors, 'createaccount' ) );
803 - // Stuff that might want to be added at the end. For example, instruc-
804 - // tions if blocked.
805 - $wgOut->addWikiMsg( 'cantcreateaccount-nonblock-text' );
806 -
807 - $wgOut->returnToMain( false );
808 - }
809 -
810 - /** */
811 - function userBlockedMessage() {
812 - global $wgOut, $wgUser;
813 -
814 - # Let's be nice about this, it's likely that this feature will be used
815 - # for blocking large numbers of innocent people, e.g. range blocks on
816 - # schools. Don't blame it on the user. There's a small chance that it
817 - # really is the user's fault, i.e. the username is blocked and they
818 - # haven't bothered to log out before trying to create an account to
819 - # evade it, but we'll leave that to their guilty conscience to figure
820 - # out.
821 -
822 - $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
823 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
824 - $wgOut->setArticleRelated( false );
825 -
826 - $ip = wfGetIP();
827 - $blocker = User::whoIs( $wgUser->mBlock->mBy );
828 - $block_reason = $wgUser->mBlock->mReason;
829 -
830 - if ( strval( $block_reason ) === '' ) {
831 - $block_reason = wfMsg( 'blockednoreason' );
832 - }
833 - $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
834 - $wgOut->returnToMain( false );
835 - }
836 -
837 - /**
838 - * @private
839 - */
840 - function mainLoginForm( $msg, $msgtype = 'error' ) {
841 - global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
842 - global $wgCookiePrefix, $wgLoginLanguageSelector;
843 - global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
844 -
845 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
846 -
847 - if ( $this->mType == 'signup' ) {
848 - // Block signup here if in readonly. Keeps user from
849 - // going through the process (filling out data, etc)
850 - // and being informed later.
851 - if ( wfReadOnly() ) {
 491+ switch( $result ){
 492+ case Login::MAIL_READ_ONLY :
852493 $wgOut->readOnlyPage();
853494 return;
854 - } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
855 - $this->userBlockedMessage();
 495+ case Login::MAIL_PASSCHANGE_FORBIDDEN:
 496+ $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
856497 return;
857 - } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
858 - $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
 498+ case Login::MAIL_BLOCKED:
 499+ $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
859500 return;
860 - }
 501+ case Login::MAIL_PING_THROTTLED:
 502+ $wgOut->rateLimited();
 503+ return;
 504+ case Login::MAIL_PASS_THROTTLED:
 505+ global $wgPasswordReminderResendTime;
 506+ # Round the time in hours to 3 d.p., in case someone
 507+ # is specifying minutes or seconds.
 508+ $this->mainLoginForm( wfMsgExt(
 509+ 'throttled-mailpassword',
 510+ array( 'parsemag' ),
 511+ round( $wgPasswordReminderResendTime, 3 )
 512+ ) );
 513+ return;
 514+ case Login::NO_NAME:
 515+ $this->mainLoginForm( wfMsg( 'noname' ) );
 516+ return;
 517+ case Login::NOT_EXISTS:
 518+ $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mLogin->mUser->getName() ) ) );
 519+ return;
 520+ case Login::MAIL_EMPTY_EMAIL:
 521+ $this->mainLoginForm( wfMsg( 'noemail', $this->mLogin->mUser->getName() ) );
 522+ return;
 523+ case Login::MAIL_BAD_IP:
 524+ $this->mainLoginForm( wfMsg( 'badipaddress' ) );
 525+ return;
 526+ case Login::MAIL_ERROR:
 527+ $this->mainLoginForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
 528+ return;
 529+ case Login::SUCCESS:
 530+ $this->mainLoginForm( wfMsg( 'passwordsent', $this->mLogin->mUser->getName() ), 'success' );
 531+ return;
861532 }
862 -
863 - if ( '' == $this->mName ) {
864 - if ( $wgUser->isLoggedIn() ) {
865 - $this->mName = $wgUser->getName();
866 - } else {
867 - $this->mName = isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ? $_COOKIE[$wgCookiePrefix.'UserName'] : null;
868 - }
869 - }
870 -
871 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
872 -
873 - if ( $this->mType == 'signup' ) {
874 - $template = new UsercreateTemplate();
875 - $q = 'action=submitlogin&type=signup';
876 - $linkq = 'type=login';
877 - $linkmsg = 'gotaccount';
878 - } else {
879 - $template = new UserloginTemplate();
880 - $q = 'action=submitlogin&type=login';
881 - $linkq = 'type=signup';
882 - $linkmsg = 'nologin';
883 - }
884 -
885 - if ( !empty( $this->mReturnTo ) ) {
886 - $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
887 - if ( !empty( $this->mReturnToQuery ) )
888 - $returnto .= '&returntoquery=' .
889 - wfUrlencode( $this->mReturnToQuery );
890 - $q .= $returnto;
891 - $linkq .= $returnto;
892 - }
893 -
894 - # Pass any language selection on to the mode switch link
895 - if( $wgLoginLanguageSelector && $this->mLanguage )
896 - $linkq .= '&uselang=' . $this->mLanguage;
897 -
898 - $link = '<a href="' . htmlspecialchars ( $titleObj->getLocalUrl( $linkq ) ) . '">';
899 - $link .= wfMsgHtml( $linkmsg . 'link' ); # Calling either 'gotaccountlink' or 'nologinlink'
900 - $link .= '</a>';
901 -
902 - # Don't show a "create account" link if the user can't
903 - if( $this->showCreateOrLoginLink( $wgUser ) )
904 - $template->set( 'link', wfMsgWikiHtml( $linkmsg, $link ) );
905 - else
906 - $template->set( 'link', '' );
907 -
908 - $template->set( 'header', '' );
909 - $template->set( 'name', $this->mName );
910 - $template->set( 'password', $this->mPassword );
911 - $template->set( 'retype', $this->mRetype );
912 - $template->set( 'email', $this->mEmail );
913 - $template->set( 'realname', $this->mRealName );
914 - $template->set( 'domain', $this->mDomain );
915 -
916 - $template->set( 'action', $titleObj->getLocalUrl( $q ) );
917 - $template->set( 'message', $msg );
918 - $template->set( 'messagetype', $msgtype );
919 - $template->set( 'createemail', $wgEnableEmail && $wgUser->isLoggedIn() );
920 - $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) );
921 - $template->set( 'useemail', $wgEnableEmail );
922 - $template->set( 'emailrequired', $wgEmailConfirmToEdit );
923 - $template->set( 'canreset', $wgAuth->allowPasswordChange() );
924 - $template->set( 'canremember', ( $wgCookieExpiration > 0 ) );
925 - $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember );
926 -
927 - # Prepare language selection links as needed
928 - if( $wgLoginLanguageSelector ) {
929 - $template->set( 'languages', $this->makeLanguageSelector() );
930 - if( $this->mLanguage )
931 - $template->set( 'uselang', $this->mLanguage );
932 - }
933 -
934 - // Give authentication and captcha plugins a chance to modify the form
935 - $wgAuth->modifyUITemplate( $template, $this->mType );
936 - if ( $this->mType == 'signup' ) {
937 - wfRunHooks( 'UserCreateForm', array( &$template ) );
938 - } else {
939 - wfRunHooks( 'UserLoginForm', array( &$template ) );
940 - }
941 -
942 - $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
943 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
944 - $wgOut->setArticleRelated( false );
945 - $wgOut->disallowUserJs(); // just in case...
946 - $wgOut->addTemplate( $template );
947533 }
948534
949535 /**
950 - * @private
 536+ * Since the UserLoginForm hook was changed to pass a SpecialPage
 537+ * instead of a QuickTemplate derivative, old extensions might
 538+ * easily try calling this method expecing it to exist. Tempting
 539+ * though it is to let them have the fatal error, let's at least
 540+ * fail gracefully...
 541+ * @deprecated
951542 */
952 - function showCreateOrLoginLink( &$user ) {
953 - if( $this->mType == 'signup' ) {
954 - return( true );
955 - } elseif( $user->isAllowed( 'createaccount' ) ) {
956 - return( true );
957 - } else {
958 - return( false );
959 - }
 543+ public function set(){
 544+ wfDeprecated( __METHOD__ );
960545 }
961 -
962 - /**
963 - * Check if a session cookie is present.
964 - *
965 - * This will not pick up a cookie set during _this_ request, but is meant
966 - * to ensure that the client is returning the cookie which was set on a
967 - * previous pass through the system.
968 - *
969 - * @private
970 - */
971 - function hasSessionCookie() {
972 - global $wgDisableCookieCheck, $wgRequest;
973 - return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
974 - }
975 -
976 - /**
977 - * @private
978 - */
979 - function cookieRedirectCheck( $type ) {
980 - global $wgOut;
981 -
982 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
983 - $query = array( 'wpCookieCheck' => $type );
984 - if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
985 - $check = $titleObj->getFullURL( $query );
986 -
987 - return $wgOut->redirect( $check );
988 - }
989 -
990 - /**
991 - * @private
992 - */
993 - function onCookieRedirectCheck( $type ) {
994 - if ( !$this->hasSessionCookie() ) {
995 - if ( $type == 'new' ) {
996 - return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
997 - } else if ( $type == 'login' ) {
998 - return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
999 - } else {
1000 - # shouldn't happen
1001 - return $this->mainLoginForm( wfMsg( 'error' ) );
1002 - }
1003 - } else {
1004 - return $this->successfulLogin();
1005 - }
1006 - }
1007 -
1008 - /**
1009 - * @private
1010 - */
1011 - function throttleHit( $limit ) {
1012 - $this->mainLoginForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $limit ) );
1013 - }
1014 -
1015 - /**
1016 - * Produce a bar of links which allow the user to select another language
1017 - * during login/registration but retain "returnto"
1018 - *
1019 - * @return string
1020 - */
1021 - function makeLanguageSelector() {
1022 - global $wgLang;
1023 -
1024 - $msg = wfMsgForContent( 'loginlanguagelinks' );
1025 - if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
1026 - $langs = explode( "\n", $msg );
1027 - $links = array();
1028 - foreach( $langs as $lang ) {
1029 - $lang = trim( $lang, '* ' );
1030 - $parts = explode( '|', $lang );
1031 - if (count($parts) >= 2) {
1032 - $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
1033 - }
1034 - }
1035 - return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
1036 - } else {
1037 - return '';
1038 - }
1039 - }
1040 -
1041 - /**
1042 - * Create a language selector link for a particular language
1043 - * Links back to this page preserving type and returnto
1044 - *
1045 - * @param $text Link text
1046 - * @param $lang Language code
1047 - */
1048 - function makeLanguageSelectorLink( $text, $lang ) {
1049 - global $wgUser;
1050 - $self = SpecialPage::getTitleFor( 'Userlogin' );
1051 - $attr = array( 'uselang' => $lang );
1052 - if( $this->mType == 'signup' )
1053 - $attr['type'] = 'signup';
1054 - if( $this->mReturnTo )
1055 - $attr['returnto'] = $this->mReturnTo;
1056 - $skin = $wgUser->getSkin();
1057 - return $skin->linkKnown(
1058 - $self,
1059 - htmlspecialchars( $text ),
1060 - array(),
1061 - $attr
1062 - );
1063 - }
1064546 }
Index: branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php
@@ -0,0 +1,641 @@
 2+<?php
 3+/**
 4+ * Special page for creating/registering new user accounts.
 5+ * @ingroup SpecialPage
 6+ */
 7+class SpecialCreateAccount extends SpecialPage {
 8+
 9+ var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted;
 10+ var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage;
 11+ var $mReturnToQuery;
 12+
 13+ public $mDomains = array();
 14+
 15+ public $mUseEmail = true; # Can be switched off by AuthPlugins etc
 16+ public $mUseRealname = true;
 17+ public $mUseRemember = true;
 18+
 19+ public $mFormHeader = '';
 20+ public $mFormFields = array(
 21+ 'Name' => array(
 22+ 'type' => 'text',
 23+ 'label-message' => 'yourname',
 24+ 'id' => 'wpName2',
 25+ 'tabindex' => '1',
 26+ 'size' => '20',
 27+ 'required' => '1',
 28+ 'autofocus' => '',
 29+ ),
 30+ 'Password' => array(
 31+ 'type' => 'password',
 32+ 'label-message' => 'yourpassword',
 33+ 'size' => '20',
 34+ 'id' => 'wpPassword2',
 35+ 'required' => '',
 36+ ),
 37+ 'Retype' => array(
 38+ 'type' => 'password',
 39+ 'label-message' => 'yourpasswordagain',
 40+ 'size' => '20',
 41+ 'id' => 'wpRetype',
 42+ 'required' => '',
 43+ ),
 44+ 'Email' => array(
 45+ 'type' => 'email',
 46+ 'label-message' => 'youremail',
 47+ 'size' => '20',
 48+ 'id' => 'wpEmail',
 49+ ),
 50+ 'RealName' => array(
 51+ 'type' => 'text',
 52+ 'label-message' => 'yourrealname',
 53+ 'id' => 'wpRealName',
 54+ 'tabindex' => '1',
 55+ 'size' => '20',
 56+ ),
 57+ 'Remember' => array(
 58+ 'type' => 'check',
 59+ 'label-message' => 'remembermypassword',
 60+ 'id' => 'wpRemember',
 61+ ),
 62+ 'Domain' => array(
 63+ 'type' => 'select',
 64+ 'id' => 'wpDomain',
 65+ 'label-message' => 'yourdomainname',
 66+ 'options' => null,
 67+ 'default' => null,
 68+ ),
 69+ );
 70+
 71+ public function __construct(){
 72+ parent::__construct( 'CreateAccount', 'createaccount' );
 73+ $this->mLogin = new Login();
 74+ $this->mFormFields['RealName']['help'] = wfMsg( 'prefs-help-realname' );
 75+ }
 76+
 77+ public function execute( $par ){
 78+ global $wgUser, $wgOut;
 79+
 80+ $this->setHeaders();
 81+ $this->loadQuery();
 82+
 83+ # Block signup here if in readonly. Keeps user from
 84+ # going through the process (filling out data, etc)
 85+ # and being informed later.
 86+ if ( wfReadOnly() ) {
 87+ $wgOut->readOnlyPage();
 88+ return;
 89+ }
 90+ # Bail out straightaway on permissions errors
 91+ if ( !$this->userCanExecute( $wgUser ) ) {
 92+ $this->displayRestrictionError();
 93+ return;
 94+ } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
 95+ $this->userBlockedMessage();
 96+ return;
 97+ } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
 98+ var_dump('error');
 99+ $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
 100+ return;
 101+ }
 102+
 103+ if( $this->mPosted ) {
 104+ if ( $this->mCreateaccountMail ) {
 105+ return $this->addNewAccountMailPassword();
 106+ } else {
 107+ return $this->addNewAccount();
 108+ }
 109+ } else {
 110+ $this->showMainForm('');
 111+ }
 112+ }
 113+
 114+ /**
 115+ * Load the member variables from the request parameters
 116+ */
 117+ protected function loadQuery(){
 118+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 119+ $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
 120+ && $wgEnableEmail;
 121+
 122+ $this->mUsername = $wgRequest->getText( 'wpName' );
 123+ $this->mPassword = $wgRequest->getText( 'wpPassword' );
 124+ $this->mRetype = $wgRequest->getText( 'wpRetype' );
 125+ $this->mDomain = $wgRequest->getText( 'wpDomain' );
 126+ $this->mReturnTo = $wgRequest->getVal( 'returnto' );
 127+ $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 128+ $this->mPosted = $wgRequest->wasPosted();
 129+ $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
 130+ && $wgEnableEmail;
 131+ $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
 132+ $this->mLanguage = $wgRequest->getText( 'uselang' );
 133+
 134+ if ( $wgRedirectOnLogin ) {
 135+ $this->mReturnTo = $wgRedirectOnLogin;
 136+ $this->mReturnToQuery = '';
 137+ }
 138+
 139+ if( $wgEnableEmail ) {
 140+ $this->mEmail = $wgRequest->getText( 'wpEmail' );
 141+ } else {
 142+ $this->mEmail = '';
 143+ }
 144+ if( !in_array( 'realname', $wgHiddenPrefs ) ) {
 145+ $this->mRealName = $wgRequest->getText( 'wpRealName' );
 146+ } else {
 147+ $this->mRealName = '';
 148+ }
 149+
 150+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 151+ $this->mDomain = 'invaliddomain';
 152+ }
 153+ $wgAuth->setDomain( $this->mDomain );
 154+
 155+ # When switching accounts, it sucks to get automatically logged out
 156+ $returnToTitle = Title::newFromText( $this->mReturnTo );
 157+ if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
 158+ $this->mReturnTo = '';
 159+ $this->mReturnToQuery = '';
 160+ }
 161+ }
 162+
 163+ /**
 164+ * Add a new account, and mail its password to the user
 165+ */
 166+ protected function addNewAccountMailPassword() {
 167+ global $wgOut;
 168+
 169+ if( !$this->mEmail ) {
 170+ $this->showMainForm( wfMsg( 'noemail', htmlspecialchars( $this->mUsername ) ) );
 171+ return;
 172+ }
 173+
 174+ if( !$this->addNewaccountInternal() ) {
 175+ return;
 176+ }
 177+
 178+ # Wipe the initial password
 179+ $this->mLogin->mUser->setPassword( null );
 180+ $this->mLogin->mUser->saveSettings();
 181+
 182+ # And mail them a temporary one
 183+ $result = $this->mLogin->mailPassword( 'createaccount-title', 'createaccount-text' );
 184+
 185+ wfRunHooks( 'AddNewAccount', array( $this->mLogin->mUser, true ) );
 186+ $this->mLogin->mUser->addNewUserLogEntry();
 187+
 188+ $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
 189+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 190+ $wgOut->setArticleRelated( false );
 191+
 192+ if( $result != Login::SUCCESS ) {
 193+ if( $result == Login::MAIL_ERROR ){
 194+ $this->showMainForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
 195+ } else {
 196+ $this->showMainForm( wfMsg( 'mailerror' ) );
 197+ }
 198+ } else {
 199+ $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUser->getName(), $this->mLogin->mUser->getEmail() );
 200+ $wgOut->returnToMain( false );
 201+ }
 202+ }
 203+
 204+ /**
 205+ * Create a new user account from the provided data
 206+ */
 207+ protected function addNewAccount() {
 208+ global $wgUser, $wgEmailAuthentication;
 209+
 210+ # Create the account and abort if there's a problem doing so
 211+ if( !$this->addNewAccountInternal() )
 212+ return;
 213+ $user = $this->mLogin->mUser;
 214+
 215+ # If we showed up language selection links, and one was in use, be
 216+ # smart (and sensible) and save that language as the user's preference
 217+ global $wgLoginLanguageSelector;
 218+ if( $wgLoginLanguageSelector && $this->mLanguage )
 219+ $user->setOption( 'language', $this->mLanguage );
 220+
 221+ # Send out an email authentication message if needed
 222+ if( $wgEmailAuthentication && User::isValidEmailAddr( $user->getEmail() ) ) {
 223+ global $wgOut;
 224+ $error = $user->sendConfirmationMail();
 225+ if( WikiError::isError( $error ) ) {
 226+ $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
 227+ } else {
 228+ $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 229+ }
 230+ }
 231+
 232+ # Save settings (including confirmation token)
 233+ $user->saveSettings();
 234+
 235+ # If not logged in, assume the new account as the current one and set
 236+ # session cookies then show a "welcome" message or a "need cookies"
 237+ # message as needed
 238+ if( $wgUser->isAnon() ) {
 239+ $wgUser = $user;
 240+ $wgUser->setCookies();
 241+ wfRunHooks( 'AddNewAccount', array( $wgUser ) );
 242+ $wgUser->addNewUserLogEntry();
 243+ if( $this->hasSessionCookie() ) {
 244+ return $this->successfulCreation();
 245+ } else {
 246+ return $this->cookieRedirectCheck();
 247+ }
 248+ } else {
 249+ # Confirm that the account was created
 250+ global $wgOut;
 251+ $self = SpecialPage::getTitleFor( 'Userlogin' );
 252+ $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
 253+ $wgOut->setArticleRelated( false );
 254+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 255+ $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $user->getName() ) );
 256+ $wgOut->returnToMain( false, $self );
 257+ wfRunHooks( 'AddNewAccount', array( $user ) );
 258+ $user->addNewUserLogEntry();
 259+ return true;
 260+ }
 261+ }
 262+
 263+ /**
 264+ * Deeper mechanics of initialising a new user and passing it
 265+ * off to Login::initUser()
 266+ * return Bool whether the user was successfully created
 267+ */
 268+ protected function addNewAccountInternal() {
 269+ global $wgUser, $wgOut;
 270+ global $wgEnableSorbs, $wgProxyWhitelist;
 271+ global $wgMemc, $wgAccountCreationThrottle;
 272+ global $wgAuth, $wgMinimalPasswordLength;
 273+ global $wgEmailConfirmToEdit;
 274+
 275+ # If the user passes an invalid domain, something is fishy
 276+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 277+ $this->showMainForm( wfMsg( 'wrongpassword' ) );
 278+ return false;
 279+ }
 280+
 281+ # If we are not allowing users to login locally, we should be checking
 282+ # to see if the user is actually able to authenticate to the authenti-
 283+ # cation server before they create an account (otherwise, they can
 284+ # create a local account and login as any domain user). We only need
 285+ # to check this for domains that aren't local.
 286+ if( !in_array( $this->mDomain, array( 'local', '' ) )
 287+ && !$wgAuth->canCreateAccounts()
 288+ && ( !$wgAuth->userExists( $this->mUsername )
 289+ || !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
 290+ ) )
 291+ {
 292+ $this->showMainForm( wfMsg( 'wrongpassword' ) );
 293+ return false;
 294+ }
 295+
 296+ $ip = wfGetIP();
 297+ if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
 298+ $wgUser->inSorbsBlacklist( $ip ) )
 299+ {
 300+ $this->showMainForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
 301+ return false;
 302+ }
 303+
 304+ # Now create a dummy user ($user) and check if it is valid
 305+ $name = trim( $this->mUsername );
 306+ $user = User::newFromName( $name, 'creatable' );
 307+ if ( is_null( $user ) ) {
 308+ $this->showMainForm( wfMsg( 'noname' ) );
 309+ return false;
 310+ }
 311+
 312+ if ( 0 != $user->idForName() ) {
 313+ $this->showMainForm( wfMsg( 'userexists' ) );
 314+ return false;
 315+ }
 316+
 317+ if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
 318+ $this->showMainForm( wfMsg( 'badretype' ) );
 319+ return false;
 320+ }
 321+
 322+ # check for minimal password length
 323+ $valid = $user->isValidPassword( $this->mPassword );
 324+ if ( $valid !== true ) {
 325+ if ( !$this->mCreateaccountMail ) {
 326+ $this->showMainForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
 327+ return false;
 328+ } else {
 329+ # do not force a password for account creation by email
 330+ # set invalid password, it will be replaced later by a random generated password
 331+ $this->mPassword = null;
 332+ }
 333+ }
 334+
 335+ # if you need a confirmed email address to edit, then obviously you
 336+ # need an email address.
 337+ if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
 338+ $this->showMainForm( wfMsg( 'noemailtitle' ) );
 339+ return false;
 340+ }
 341+
 342+ if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
 343+ $this->showMainForm( wfMsg( 'invalidemailaddress' ) );
 344+ return false;
 345+ }
 346+
 347+ # Set some additional data so the AbortNewAccount hook can be used for
 348+ # more than just username validation
 349+ $user->setEmail( $this->mEmail );
 350+ $user->setRealName( $this->mRealName );
 351+
 352+ $abortError = '';
 353+ if( !wfRunHooks( 'AbortNewAccount', array( $user, &$abortError ) ) ) {
 354+ # Hook point to add extra creation throttles and blocks
 355+ wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
 356+ $this->showMainForm( $abortError );
 357+ return false;
 358+ }
 359+
 360+ if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
 361+ $key = wfMemcKey( 'acctcreate', 'ip', $ip );
 362+ $value = $wgMemc->get( $key );
 363+ if ( !$value ) {
 364+ $wgMemc->set( $key, 0, 86400 );
 365+ }
 366+ if ( $value >= $wgAccountCreationThrottle ) {
 367+ $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
 368+ return false;
 369+ }
 370+ $wgMemc->incr( $key );
 371+ }
 372+
 373+ if( !$wgAuth->addUser( $user, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
 374+ $this->showMainForm( wfMsg( 'externaldberror' ) );
 375+ return false;
 376+ }
 377+
 378+ $this->mLogin->mUser = $user;
 379+ $this->mLogin->initUser( false );
 380+ return true;
 381+ }
 382+
 383+ /**
 384+ * Run any hooks registered for logins, then
 385+ * display a message welcoming the user.
 386+ */
 387+ protected function successfulCreation(){
 388+ global $wgUser, $wgOut;
 389+
 390+ # Run any hooks; display injected HTML
 391+ $injected_html = '';
 392+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
 393+
 394+ SpecialUserLogin::displaySuccessfulLogin(
 395+ 'welcomecreation',
 396+ $injected_html,
 397+ $this->mReturnTo,
 398+ $this->mReturnToQuery );
 399+ }
 400+
 401+ /**
 402+ * Display a message indicating that account creation from their IP has
 403+ * been blocked by a (range)block with 'block account creation' enabled.
 404+ * It's likely that this feature will be used for blocking large numbers
 405+ * of innocent people, e.g. range blocks on schools. Don't blame it on
 406+ * the user. There's a small chance that it really is the user's fault,
 407+ * i.e. the username is blocked and they haven't bothered to log out
 408+ * before trying to create an account to evade it, but we'll leave that
 409+ * to their guilty conscience to figure out...
 410+ */
 411+ protected function userBlockedMessage() {
 412+ global $wgOut, $wgUser;
 413+
 414+ $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
 415+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 416+ $wgOut->setArticleRelated( false );
 417+
 418+ $ip = wfGetIP();
 419+ $blocker = User::whoIs( $wgUser->mBlock->mBy );
 420+ $block_reason = $wgUser->mBlock->mReason;
 421+
 422+ if ( strval( $block_reason ) === '' ) {
 423+ $block_reason = wfMsg( 'blockednoreason' );
 424+ }
 425+ $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
 426+ $wgOut->returnToMain( false );
 427+ }
 428+
 429+ /**
 430+ * Show the main input form, with an appropriate error message
 431+ * from a previous iteration, if necessary
 432+ * @param $msg String HTML of message received previously
 433+ * @param $msgtype String type of message, usually 'error'
 434+ */
 435+ protected function showMainForm( $msg, $msgtype = 'error' ) {
 436+ global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
 437+ global $wgCookiePrefix, $wgLoginLanguageSelector;
 438+ global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
 439+
 440+ # Parse the error message if we got one
 441+ if( $msg ){
 442+ if( $msgtype == 'error' ){
 443+ $msg = wfMsg( 'loginerror' ) . ' ' . $msg;
 444+ }
 445+ $msg = Html::rawElement(
 446+ 'div',
 447+ array( 'class' => $msgtype . 'box' ),
 448+ $msg
 449+ );
 450+ } else {
 451+ $msg = '';
 452+ }
 453+
 454+ # Make sure the returnTo strings don't get lost if the
 455+ # user changes language, etc
 456+ $linkq = array();
 457+ if ( !empty( $this->mReturnTo ) ) {
 458+ $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
 459+ if ( !empty( $this->mReturnToQuery ) )
 460+ $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
 461+ }
 462+
 463+ # Pass any language selection on to the mode switch link
 464+ if( $wgLoginLanguageSelector && $this->mLanguage )
 465+ $linkq['uselang'] = $this->mLanguage;
 466+
 467+ $skin = $wgUser->getSkin();
 468+ $link = $skin->link(
 469+ SpecialPage::getTitleFor( 'Userlogin' ),
 470+ wfMsgHtml( 'gotaccountlink' ),
 471+ array(),
 472+ $linkq );
 473+ $link = $wgUser->isLoggedIn()
 474+ ? ''
 475+ : wfMsgWikiHtml( 'gotaccount', $link );
 476+
 477+ # Prepare language selection links as needed
 478+ $langSelector = $wgLoginLanguageSelector
 479+ ? Html::rawElement(
 480+ 'div',
 481+ array( 'id' => 'languagelinks' ),
 482+ SpecialUserLogin::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
 483+ : '';
 484+
 485+ # Add a 'send password by email' button if available
 486+ $buttons = '';
 487+ if( $wgEnableEmail && $wgUser->isLoggedIn() ){
 488+ $buttons = Html::element(
 489+ 'input',
 490+ array(
 491+ 'type' => 'submit',
 492+ 'name' => 'wpCreateaccountMail',
 493+ 'value' => wfMsg( 'createaccountmail' ),
 494+ 'id' => 'wpCreateaccountMail',
 495+ )
 496+ );
 497+ }
 498+
 499+ # Give authentication and captcha plugins a chance to
 500+ # modify the form, by hook or by using $wgAuth
 501+ $wgAuth->modifyUITemplate( $this, 'new' );
 502+ wfRunHooks( 'UserCreateForm', array( &$this ) );
 503+
 504+ # The most likely use of the hook is to enable domains;
 505+ # check that now, and add fields if necessary
 506+ if( $this->mDomains ){
 507+ $this->mFormFields['Domain']['options'] = $this->mDomains;
 508+ $this->mFormFields['Domain']['default'] = $this->mDomain;
 509+ } else {
 510+ unset( $this->mFormFields['Domain'] );
 511+ }
 512+
 513+ # Or to switch email on or off
 514+ if( !$wgEnableEmail || !$this->mUseEmail ){
 515+ unset( $this->mFormFields['Email'] );
 516+ } else {
 517+ if( $wgEmailConfirmToEdit ){
 518+ $this->mFormFields['Email']['help'] = wfMsg( 'prefs-help-email-required' );
 519+ $this->mFormFields['Email']['required'] = '';
 520+ } else {
 521+ $this->mFormFields['Email']['help'] = wfMsg( 'prefs-help-email' );
 522+ }
 523+ }
 524+
 525+ # Or to play with realname
 526+ if( in_array( 'realname', $wgHiddenPrefs ) || !$this->mUseRealname ){
 527+ unset( $this->mFormFields['Realname'] );
 528+ }
 529+
 530+ # Or to tweak the 'remember my password' checkbox
 531+ if( !($wgCookieExpiration > 0) || !$this->mUseRemember ){
 532+ # Remove it altogether
 533+ unset( $this->mFormFields['Remember'] );
 534+ } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
 535+ # Or check it by default
 536+ # FIXME: this doesn't always work?
 537+ $this->mFormFields['Remember']['checked'] = '1';
 538+ }
 539+
 540+ $form = new HTMLForm( $this->mFormFields, '' );
 541+ $form->setTitle( $this->getTitle() );
 542+ $form->setSubmitText( wfMsg( 'createaccount' ) );
 543+ $form->setSubmitId( 'wpCreateaccount' );
 544+ $form->suppressReset();
 545+ $form->loadData();
 546+
 547+ $formContents = ''
 548+ . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
 549+ $link )
 550+ . $this->mFormHeader
 551+ . $langSelector
 552+ . $form->getBody()
 553+ . $form->getButtons()
 554+ . $buttons
 555+ . Xml::hidden( 'returnto', $this->mReturnTo )
 556+ . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
 557+ ;
 558+
 559+ $wgOut->setPageTitle( wfMsg( 'createaccount' ) );
 560+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 561+ $wgOut->setArticleRelated( false );
 562+ $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
 563+
 564+ $wgOut->addHTML(
 565+ Html::rawElement(
 566+ 'div',
 567+ array( 'id' => 'loginstart' ),
 568+ wfMsgExt( 'loginstart', array( 'parseinline' ) )
 569+ ) .
 570+ $msg .
 571+ Html::rawElement(
 572+ 'div',
 573+ array( 'id' => 'userloginForm' ),
 574+ $form->wrapForm( $formContents )
 575+ ) .
 576+ Html::rawElement(
 577+ 'div',
 578+ array( 'id' => 'loginend' ),
 579+ wfMsgExt( 'loginend', array( 'parseinline' ) )
 580+ )
 581+ );
 582+
 583+ }
 584+
 585+ /**
 586+ * Check if a session cookie is present.
 587+ *
 588+ * This will not pick up a cookie set during _this_ request, but is meant
 589+ * to ensure that the client is returning the cookie which was set on a
 590+ * previous pass through the system.
 591+ *
 592+ * @private
 593+ */
 594+ protected function hasSessionCookie() {
 595+ global $wgDisableCookieCheck, $wgRequest;
 596+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
 597+ }
 598+
 599+ /**
 600+ * Do a redirect back to the same page, so we can check any
 601+ * new session cookies.
 602+ */
 603+ protected function cookieRedirectCheck() {
 604+ global $wgOut;
 605+
 606+ $query = array( 'wpCookieCheck' => '1' );
 607+ if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
 608+ $check = $this->getTitle()->getFullURL( $query );
 609+
 610+ return $wgOut->redirect( $check );
 611+ }
 612+
 613+ /**
 614+ * Check the cookies and show errors if they're not enabled.
 615+ * @param $type String action being performed
 616+ */
 617+ protected function onCookieRedirectCheck() {
 618+ if ( !$this->hasSessionCookie() ) {
 619+ return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
 620+ } else {
 621+ return SpecialUserLogin::successfulLogin(
 622+ 'welcomecreate',
 623+ $this->mReturnTo,
 624+ $this->mReturnToQuery );
 625+ }
 626+ }
 627+
 628+ /**
 629+ * Since the UserCreateForm hook was changed to pass a SpecialPage
 630+ * instead of a QuickTemplate derivative, old extensions might
 631+ * easily try calling these methods expecing them to exist. Tempting
 632+ * though it is to let them have the fatal error, let's at least
 633+ * fail gracefully...
 634+ * @deprecated
 635+ */
 636+ public function set(){
 637+ wfDeprecated( __METHOD__ );
 638+ }
 639+ public function addInputItem(){
 640+ wfDeprecated( __METHOD__ );
 641+ }
 642+}
Property changes on: branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php
___________________________________________________________________
Name: svn:eol-style
1643 + native
Index: branches/happy-melon/phase3/includes/specials/SpecialResetpass.php
@@ -43,7 +43,6 @@
4444 $wgOut->addWikiMsg( 'resetpass_success' );
4545 if( !$wgUser->isLoggedIn() ) {
4646 $data = array(
47 - 'action' => 'submitlogin',
4847 'wpName' => $this->mUserName,
4948 'wpPassword' => $this->mNewpass,
5049 'returnto' => $wgRequest->getVal( 'returnto' ),
@@ -52,7 +51,7 @@
5352 $data['wpRemember'] = 1;
5453 }
5554 $login = new LoginForm( new FauxRequest( $data, true ) );
56 - $login->execute();
 55+ $login->login();
5756 }
5857 $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
5958 if ( !$titleObj instanceof Title ) {
Index: branches/happy-melon/phase3/includes/SpecialPage.php
@@ -112,8 +112,8 @@
113113 'Listredirects' => array( 'SpecialPage', 'Listredirects' ),
114114
115115 # Login/create account
116 - 'Userlogin' => array( 'SpecialPage', 'Userlogin' ),
117 - 'CreateAccount' => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
 116+ 'Userlogin' => 'SpecialUserLogin',
 117+ 'CreateAccount' => 'SpecialCreateAccount',
118118
119119 # Users and rights
120120 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ),
Index: branches/happy-melon/phase3/languages/messages/MessagesEn.php
@@ -1042,7 +1042,7 @@
10431043 'login' => 'Log in',
10441044 'nav-login-createaccount' => 'Log in / create account',
10451045 'loginprompt' => 'You must have cookies enabled to log in to {{SITENAME}}.',
1046 -'userlogin' => 'Log in / create account',
 1046+'userlogin' => 'Log in',
10471047 'logout' => 'Log out',
10481048 'userlogout' => 'Log out',
10491049 'notloggedin' => 'Not logged in',
@@ -2148,6 +2148,24 @@
21492149 'upload-unknown-size' => 'Unknown size',
21502150 'upload-http-error' => 'An HTTP error occured: $1',
21512151
 2152+# img_auth script messages
 2153+'img-auth-accessdenied' => 'Access denied',
 2154+'img-auth-nopathinfo' => 'Missing PATH_INFO.
 2155+Your server is not set up to pass this information.
 2156+It may be CGI-based and cannot support img_auth.
 2157+See http://www.mediawiki.org/wiki/Manual:Image_Authorization.',
 2158+'img-auth-notindir' => 'Requested path is not in the configured upload directory.',
 2159+'img-auth-badtitle' => 'Unable to construct a valid title from "$1".',
 2160+'img-auth-nologinnWL' => 'You are not logged in and "$1" is not in the whitelist.',
 2161+'img-auth-nofile' => 'File "$1" does not exist.',
 2162+'img-auth-isdir' => 'You are trying to access a directory "$1".
 2163+Only file access is allowed.',
 2164+'img-auth-streaming' => 'Streaming "$1".',
 2165+'img-auth-public' => 'The function of img_auth.php is to output files from a private wiki.
 2166+This wiki is configured as a public wiki.
 2167+For optimal security, img_auth.php is disabled.',
 2168+'img-auth-noread' => 'User does not have access to read "$1".',
 2169+
21522170 # Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
21532171 'upload-curl-error6' => 'Could not reach URL',
21542172 'upload-curl-error6-text' => 'The URL provided could not be reached.
@@ -4176,19 +4194,4 @@
41774195 'htmlform-reset' => 'Undo changes',
41784196 'htmlform-selectorother-other' => 'Other',
41794197
4180 -# Add categories per AJAX
4181 -'ajax-add-category' => 'Add category',
4182 -'ajax-add-category-submit' => 'Add',
4183 -'ajax-confirm-title' => 'Confirm action',
4184 -'ajax-confirm-prompt' => 'You can provide an edit summary below.
4185 -Click "Save" to save your edit.',
4186 -'ajax-confirm-save' => 'Save',
4187 -'ajax-add-category-summary' => 'Add category "$1"',
4188 -'ajax-remove-category-summary' => 'Remove category "$1"',
4189 -'ajax-confirm-actionsummary' => 'Action to take:',
4190 -'ajax-error-title' => 'Error',
4191 -'ajax-error-dismiss' => 'OK',
4192 -'ajax-remove-category-error' => 'It was not possible to remove this category.
4193 -This usually occurs when the category has been added to the page in a template.',
4194 -
41954198 );

Status & tagging log