Index: trunk/extensions/ConfirmAccount/dataclasses/UserAccountRequest.php |
— | — | @@ -248,6 +248,7 @@ |
249 | 249 | } |
250 | 250 | |
251 | 251 | /** |
| 252 | + * Try to insert the request into the database |
252 | 253 | * @return int |
253 | 254 | */ |
254 | 255 | public function insertOn() { |
— | — | @@ -303,6 +304,34 @@ |
304 | 305 | } |
305 | 306 | |
306 | 307 | /** |
| 308 | + * Try to acquire a username in the request queue for insertion |
| 309 | + * @return bool |
| 310 | + */ |
| 311 | + public static function acquireUsername( $name ) { |
| 312 | + $dbw = wfGetDB( DB_MASTER ); |
| 313 | + $conds = array( 'acr_name' => $name ); |
| 314 | + if ( $dbw->selectField( 'account_requests', '1', $conds, __METHOD__ ) ) { |
| 315 | + return false; // already in use |
| 316 | + } |
| 317 | + return !$dbw->selectField( 'account_requests', '1', |
| 318 | + $conds, __METHOD__, array( 'FOR UPDATE' ) ); // acquire LOCK |
| 319 | + } |
| 320 | + |
| 321 | + /** |
| 322 | + * Try to acquire a e-mail address in the request queue for insertion |
| 323 | + * @return bool |
| 324 | + */ |
| 325 | + public static function acquireEmail( $email ) { |
| 326 | + $dbw = wfGetDB( DB_MASTER ); |
| 327 | + $conds = array( 'acr_email' => $email ); |
| 328 | + if ( $dbw->selectField( 'account_requests', '1', $conds, __METHOD__ ) ) { |
| 329 | + return false; // already in use |
| 330 | + } |
| 331 | + return !$dbw->selectField( 'account_requests', '1', |
| 332 | + $conds, __METHOD__, array( 'FOR UPDATE' ) ); // acquire LOCK |
| 333 | + } |
| 334 | + |
| 335 | + /** |
307 | 336 | * Flatten areas of interest array |
308 | 337 | * Used by ConfirmAccountsPage |
309 | 338 | * @param $areas Array |
Index: trunk/extensions/ConfirmAccount/business/AccountRequestSubmission.php |
— | — | @@ -60,13 +60,14 @@ |
61 | 61 | */ |
62 | 62 | public function submit( IContextSource $context ) { |
63 | 63 | global $wgAuth, $wgAccountRequestThrottle, $wgMemc, $wgContLang; |
| 64 | + global $wgAccountRequestToS, $wgAccountRequestMinWords; |
64 | 65 | $reqUser = $this->requester; |
65 | 66 | |
66 | 67 | # Now create a dummy user ($u) and check if it is valid |
67 | 68 | if ( $this->userName === '' ) { |
68 | 69 | return array( 'accountreq_no_name', wfMsgHtml( 'noname' ) ); |
69 | 70 | } |
70 | | - $u = User::newFromName( $name, 'creatable' ); |
| 71 | + $u = User::newFromName( $this->userName, 'creatable' ); |
71 | 72 | if ( !$u ) { |
72 | 73 | return array( 'accountreq_invalid_name', wfMsgHtml( 'noname' ) ); |
73 | 74 | } |
— | — | @@ -80,19 +81,7 @@ |
81 | 82 | ); |
82 | 83 | } |
83 | 84 | } |
84 | | - # Check if already in use |
85 | | - if ( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) { |
86 | | - return array( 'accountreq_username_exists', wfMsgHtml( 'userexists' ) ); |
87 | | - } |
88 | | - # Check pending accounts for name use |
89 | | - $dbw = wfGetDB( DB_MASTER ); |
90 | | - $dup = $dbw->selectField( 'account_requests', '1', |
91 | | - array( 'acr_name' => $u->getName() ), __METHOD__ ); |
92 | | - if ( $dup ) { |
93 | | - return array( 'accountreq_username_pending', wfMsgHtml( 'requestaccount-inuse' ) ); |
94 | | - } |
95 | 85 | # Make sure user agrees to policy here |
96 | | - global $wgAccountRequestToS; |
97 | 86 | if ( $wgAccountRequestToS && !$this->tosAccepted ) { |
98 | 87 | return array( 'acct_request_skipped_tos', wfMsgHtml( 'requestaccount-agree' ) ); |
99 | 88 | } |
— | — | @@ -101,23 +90,12 @@ |
102 | 91 | return array( 'acct_request_invalid_email', wfMsgHtml( 'invalidemailaddress' ) ); |
103 | 92 | } |
104 | 93 | # Check if biography is long enough |
105 | | - global $wgAccountRequestMinWords; |
106 | 94 | if ( str_word_count( $this->bio ) < $wgAccountRequestMinWords ) { |
107 | 95 | return array( 'acct_request_short_bio', |
108 | 96 | wfMsgExt( 'requestaccount-tooshort', 'parsemag', |
109 | 97 | $wgContLang->formatNum( $wgAccountRequestMinWords ) ) |
110 | 98 | ); |
111 | 99 | } |
112 | | - # Set some additional data so the AbortNewAccount hook can be |
113 | | - # used for more than just username validation |
114 | | - $u->setEmail( $this->email ); |
115 | | - # Check if someone else has an account request with the same email |
116 | | - $dup = $dbw->selectField( 'account_requests', '1', |
117 | | - array( 'acr_email' => $u->getEmail() ), __METHOD__ ); |
118 | | - if ( $dup ) { |
119 | | - return array( 'acct_request_email_exists', wfMsgHtml( 'requestaccount-emaildup' ) ); |
120 | | - } |
121 | | - $u->setRealName( $this->realName ); |
122 | 100 | # Per security reasons, file dir cannot be pulled from client, |
123 | 101 | # so ask them to resubmit it then... |
124 | 102 | global $wgAllowAccountRequestFiles, $wgAccountRequestExtraInfo; |
— | — | @@ -133,35 +111,63 @@ |
134 | 112 | return array( false, wfMsgHtml( 'requestaccount-resub' ) ); |
135 | 113 | } |
136 | 114 | } |
| 115 | + # Check if already in use |
| 116 | + if ( 0 != $u->idForName() || $wgAuth->userExists( $u->getName() ) ) { |
| 117 | + return array( 'accountreq_username_exists', wfMsgHtml( 'userexists' ) ); |
| 118 | + } |
| 119 | + # Set email and real name |
| 120 | + $u->setEmail( $this->email ); |
| 121 | + $u->setRealName( $this->realName ); |
| 122 | + |
| 123 | + $dbw = wfGetDB( DB_MASTER ); |
| 124 | + $dbw->begin(); // ready to acquire locks |
| 125 | + # Check pending accounts for name use |
| 126 | + if ( !UserAccountRequest::acquireUsername( $u->getName() ) ) { |
| 127 | + $dbw->rollback(); |
| 128 | + return array( 'accountreq_username_pending', wfMsgHtml( 'requestaccount-inuse' ) ); |
| 129 | + } |
| 130 | + # Check if someone else has an account request with the same email |
| 131 | + if ( !UserAccountRequest::acquireEmail( $u->getEmail() ) ) { |
| 132 | + $dbw->rollback(); |
| 133 | + return array( 'acct_request_email_exists', wfMsgHtml( 'requestaccount-emaildup' ) ); |
| 134 | + } |
137 | 135 | # Process upload... |
138 | 136 | if ( $allowFiles && $this->attachmentSrcName ) { |
| 137 | + global $wgAccountRequestExts, $wgConfirmAccountFSRepos; |
| 138 | + |
139 | 139 | $ext = explode( '.', $this->attachmentSrcName ); |
140 | 140 | $finalExt = $ext[count( $ext ) - 1]; |
141 | 141 | # File must have size. |
142 | 142 | if ( trim( $this->attachmentSrcName ) == '' || empty( $this->attachmentSize ) ) { |
143 | 143 | $this->attachmentPrevName = ''; |
| 144 | + $dbw->rollback(); |
144 | 145 | return array( 'acct_request_empty_file', wfMsgHtml( 'emptyfile' ) ); |
145 | 146 | } |
146 | 147 | # Look at the contents of the file; if we can recognize the |
147 | | - # type but it's corrupt or data of the wrong type, we should |
148 | | - # probably not accept it. |
149 | | - global $wgAccountRequestExts; |
150 | | - if ( !in_array( $finalExt, $wgAccountRequestExts ) ) { |
151 | | - $this->attachmentPrevName = ''; |
| 148 | + # type but it's corrupt or data of the wrong type, we should |
| 149 | + # probably not accept it. |
| 150 | + if ( !in_array( $finalExt, $wgAccountRequestExts ) ) { |
| 151 | + $this->attachmentPrevName = ''; |
| 152 | + $dbw->rollback(); |
152 | 153 | return array( 'acct_request_bad_file_ext', wfMsgHtml( 'requestaccount-exts' ) ); |
153 | | - } |
| 154 | + } |
154 | 155 | $veri = ConfirmAccount::verifyAttachment( $this->attachmentTempPath, $finalExt ); |
155 | 156 | if ( !$veri->isGood() ) { |
156 | 157 | $this->attachmentPrevName = ''; |
157 | | - return array( 'acct_request_corrupt_file', wfMsgHtml( 'uploadcorrupt' ) ); |
| 158 | + $dbw->rollback(); |
| 159 | + return array( 'acct_request_corrupt_file', wfMsgHtml( 'verification-error' ) ); |
158 | 160 | } |
159 | 161 | # Start a transaction, move file from temp to account request directory. |
160 | | - global $wgConfirmAccountFSRepos; |
161 | 162 | $repo = new FSRepo( $wgConfirmAccountFSRepos['accountreqs'] ); |
162 | 163 | $key = sha1_file( $this->attachmentTempPath ) . '.' . $finalExt; |
163 | 164 | $pathRel = $key[0].'/'.$key[0].$key[1].'/'.$key[0].$key[1].$key[2].'/'.$key; |
164 | 165 | $triplet = array( $this->attachmentTempPath, 'public', $pathRel ); |
165 | | - $repo->storeBatch( array($triplet) ); // save! |
| 166 | + $status = $repo->storeBatch( array( $triplet ), FSRepo::OVERWRITE_SAME ); // save! |
| 167 | + if ( !$status->isOk() ) { |
| 168 | + $dbw->rollback(); |
| 169 | + return array( 'acct_request_file_store_error', |
| 170 | + wfMsgHtml( 'filecopyerror', $this->attachmentTempPath, $pathRel ) ); |
| 171 | + } |
166 | 172 | } |
167 | 173 | $expires = null; // passed by reference |
168 | 174 | $token = ConfirmAccount::getConfirmationToken( $u, $expires ); |
— | — | @@ -186,24 +192,26 @@ |
187 | 193 | 'email_token_expires' => $expires, |
188 | 194 | 'ip' => $this->ip, |
189 | 195 | ) ); |
190 | | - $dbw->begin(); |
191 | 196 | $req->insertOn(); |
192 | 197 | # Send confirmation, required! |
193 | | - $result = ConfirmAccount::sendConfirmationMail( $u, $ip, $token, $expires ); |
| 198 | + $result = ConfirmAccount::sendConfirmationMail( $u, $this->ip, $token, $expires ); |
194 | 199 | if ( !$result->isOK() ) { |
195 | | - $dbw->rollback(); // Nevermind |
| 200 | + $dbw->rollback(); // nevermind |
| 201 | + if ( isset( $repo ) && isset( $pathRel ) ) { // remove attachment |
| 202 | + $repo->cleanupBatch( array( array( 'public', $pathRel ) ) ); |
| 203 | + } |
196 | 204 | return array( 'acct_request_mail_failed', |
197 | 205 | wfMsg( 'mailerror', $context->getOutput()->parse( $result->getWikiText() ) ) ); |
198 | 206 | } |
199 | 207 | $dbw->commit(); |
| 208 | + |
200 | 209 | # Clear cache for notice of how many account requests there are |
201 | 210 | $key = wfMemcKey( 'confirmaccount', 'noticecount' ); |
202 | 211 | $wgMemc->delete( $key ); |
203 | 212 | # No request spamming... |
204 | | - # BC: check if isPingLimitable() exists |
205 | 213 | if ( $wgAccountRequestThrottle && $reqUser->isPingLimitable() ) { |
206 | 214 | $key = wfMemcKey( 'acctrequest', 'ip', $ip ); |
207 | | - $value = $wgMemc->incr( $key ); |
| 215 | + $value = $wgMemc->incr( $key ); |
208 | 216 | if ( !$value ) { |
209 | 217 | $wgMemc->set( $key, 1, 86400 ); |
210 | 218 | } |
Index: trunk/extensions/ConfirmAccount/presentation/specialpages/actions/RequestAccount_body.php |
— | — | @@ -236,6 +236,10 @@ |
237 | 237 | $this->showForm( wfMsgHtml( 'noname' ) ); |
238 | 238 | return; |
239 | 239 | } |
| 240 | + # Set some additional data so the AbortNewAccount hook can be |
| 241 | + # used for more than just username validation |
| 242 | + $u->setEmail( $this->mEmail ); |
| 243 | + $u->setRealName( $this->mRealName ); |
240 | 244 | # FIXME: Hack! If we don't want captchas for requests, temporarily turn it off! |
241 | 245 | global $wgConfirmAccountCaptchas, $wgCaptchaTriggers; |
242 | 246 | if ( !$wgConfirmAccountCaptchas && isset( $wgCaptchaTriggers ) ) { |
— | — | @@ -340,23 +344,23 @@ |
341 | 345 | } |
342 | 346 | $out->addWikiMsg( 'request-account-econf' ); |
343 | 347 | $out->returnToMain(); |
344 | | - return; |
345 | | - } |
346 | | - # Maybe the user confirmed after account was created... |
347 | | - $user = User::newFromConfirmationCode( $code ); |
348 | | - if ( is_object( $user ) ) { |
349 | | - if ( $user->confirmEmail() ) { |
350 | | - $message = $reqUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success'; |
351 | | - $out->addWikiMsg( $message ); |
352 | | - if ( !$reqUser->isLoggedIn() ) { |
353 | | - $title = SpecialPage::getTitleFor( 'Userlogin' ); |
354 | | - $out->returnToMain( true, $title->getPrefixedUrl() ); |
| 348 | + } else { |
| 349 | + # Maybe the user confirmed after account was created... |
| 350 | + $user = User::newFromConfirmationCode( $code ); |
| 351 | + if ( is_object( $user ) ) { |
| 352 | + if ( $user->confirmEmail() ) { |
| 353 | + $message = $reqUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success'; |
| 354 | + $out->addWikiMsg( $message ); |
| 355 | + if ( !$reqUser->isLoggedIn() ) { |
| 356 | + $title = SpecialPage::getTitleFor( 'Userlogin' ); |
| 357 | + $out->returnToMain( true, $title->getPrefixedUrl() ); |
| 358 | + } |
| 359 | + } else { |
| 360 | + $out->addWikiMsg( 'confirmemail_error' ); |
355 | 361 | } |
356 | 362 | } else { |
357 | | - $out->addWikiMsg( 'confirmemail_error' ); |
| 363 | + $out->addWikiMsg( 'confirmemail_invalid' ); |
358 | 364 | } |
359 | | - } else { |
360 | | - $out->addWikiMsg( 'confirmemail_invalid' ); |
361 | 365 | } |
362 | 366 | } |
363 | 367 | } |