/**
  * Move old stale requests to rejected list. Delete old rejected requests.
  */
 public static function runAutoMaintenance()
 {
     global $wgRejectedAccountMaxAge, $wgConfirmAccountRejectAge, $wgConfirmAccountFSRepos;
     $dbw = wfGetDB(DB_MASTER);
     $repo = new FSRepo($wgConfirmAccountFSRepos['accountreqs']);
     # Select all items older than time $encCutoff
     $encCutoff = $dbw->addQuotes($dbw->timestamp(time() - $wgRejectedAccountMaxAge));
     $res = $dbw->select('account_requests', array('acr_id', 'acr_storage_key'), array("acr_rejected < {$encCutoff}"), __METHOD__);
     # Clear out any associated attachments and delete those rows
     foreach ($res as $row) {
         $key = $row->acr_storage_key;
         if ($key) {
             $path = $repo->getZonePath('public') . '/' . UserAccountRequest::relPathFromKey($key);
             if ($path && file_exists($path)) {
                 unlink($path);
             }
         }
         $dbw->delete('account_requests', array('acr_id' => $row->acr_id), __METHOD__);
     }
     # Select all items older than time $encCutoff
     $encCutoff = $dbw->addQuotes($dbw->timestamp(time() - $wgConfirmAccountRejectAge));
     # Old stale accounts will count as rejected. If the request was held, give it more time.
     $dbw->update('account_requests', array('acr_rejected' => $dbw->timestamp(), 'acr_user' => 0, 'acr_comment' => wfMsgForContent('confirmaccount-autorej'), 'acr_deleted' => 1), array("acr_rejected IS NULL", "acr_registration < {$encCutoff}", "acr_held < {$encCutoff} OR acr_held IS NULL"), __METHOD__);
     # Clear cache for notice of how many account requests there are
     self::clearAccountRequestCountCache();
 }
 /**
  * @param $user User
  * @param $abortError
  * @return bool
  */
 public static function checkIfAccountNameIsPending(User $user, &$abortError)
 {
     # If an account is made with name X, and one is pending with name X
     # we will have problems if the pending one is later confirmed
     if (!UserAccountRequest::acquireUsername($user->getName())) {
         $abortError = wfMsgHtml('requestaccount-inuse');
         return false;
     }
     return true;
 }
 /**
  * Get requested account request row and load some fields
  * @param $id int
  * @param $wasPosted bool
  * @return void
  */
 protected function loadAccountRequest($id, $wasPosted)
 {
     $from = $wasPosted ? 'dbmaster' : 'dbslave';
     $this->accountReq = UserAccountRequest::newFromId($id, $from);
     # Check if parameters are to be overridden
     if ($this->accountReq) {
         $this->reqUsername = $this->reqUsername != '' ? $this->reqUsername : $this->accountReq->getName();
         $this->reqBio = $this->reqBio != '' ? $this->reqBio : $this->accountReq->getBio();
         $this->reqType = !is_null($this->reqType) ? $this->reqType : $this->accountReq->getType();
         $origAreas = $this->accountReq->getAreas();
         foreach ($this->reqAreas as $area => $within) {
             # If admin didn't set any of these checks, go back to how the user set them.
             # On GET requests, the admin probably didn't set anything.
             if ($within == -1) {
                 if (in_array($area, $origAreas)) {
                     $this->reqAreas[$area] = 1;
                 } else {
                     $this->reqAreas[$area] = 0;
                 }
             }
         }
     }
 }
 protected function acceptRequest(IContextSource $context)
 {
     global $wgAuth, $wgAccountRequestTypes, $wgConfirmAccountSaveInfo;
     global $wgAllowAccountRequestFiles, $wgConfirmAccountFSRepos;
     $accReq = $this->accountReq;
     // convenience
     # Now create user and check if the name is valid
     $user = User::newFromName($this->userName, 'creatable');
     if (!$user) {
         return array('accountconf_invalid_name', wfMsgHtml('noname'));
     }
     # Check if account name is already in use
     if (0 != $user->idForName() || $wgAuth->userExists($user->getName())) {
         return array('accountconf_user_exists', wfMsgHtml('userexists'));
     }
     $dbw = wfGetDB(DB_MASTER);
     $dbw->begin();
     # Make a random password
     $p = User::randomPassword();
     # Insert the new user into the DB...
     $tokenExpires = $accReq->getEmailTokenExpires();
     $authenticated = $accReq->getEmailAuthTimestamp();
     $params = array('real_name' => $accReq->getRealName(), 'newpassword' => User::crypt($p), 'email' => $accReq->getEmail(), 'email_authenticated' => $dbw->timestampOrNull($authenticated), 'email_token_expires' => $dbw->timestamp($tokenExpires), 'email_token' => $accReq->getEmailToken());
     $user = User::createNew($user->getName(), $params);
     # Grant any necessary rights (exclude blank or dummy groups)
     $group = self::getGroupFromType($this->type);
     if ($group != '' && $group != 'user' && $group != '*') {
         $user->addGroup($group);
     }
     $acd_id = null;
     // used for rollback cleanup
     # Save account request data to credentials system
     if ($wgConfirmAccountSaveInfo) {
         $key = $accReq->getFileStorageKey();
         # Copy any attached files to new storage group
         if ($wgAllowAccountRequestFiles && $key) {
             $repoOld = new FSRepo($wgConfirmAccountFSRepos['accountreqs']);
             $repoNew = new FSRepo($wgConfirmAccountFSRepos['accountcreds']);
             $pathRel = UserAccountRequest::relPathFromKey($key);
             $oldPath = $repoOld->getZonePath('public') . '/' . $pathRel;
             $triplet = array($oldPath, 'public', $pathRel);
             $status = $repoNew->storeBatch(array($triplet));
             // copy!
             if (!$status->isOK()) {
                 $dbw->rollback();
                 # DELETE new rows in case there was a COMMIT somewhere
                 $this->acceptRequest_rollback($dbw, $user->getId(), $acd_id);
                 return array('accountconf_copyfailed', $context->getOutput()->parse($status->getWikiText()));
             }
         }
         $acd_id = $dbw->nextSequenceValue('account_credentials_acd_id_seq');
         # Move request data into a separate table
         $dbw->insert('account_credentials', array('acd_user_id' => $user->getID(), 'acd_real_name' => $accReq->getRealName(), 'acd_email' => $accReq->getEmail(), 'acd_email_authenticated' => $dbw->timestampOrNull($authenticated), 'acd_bio' => $accReq->getBio(), 'acd_notes' => $accReq->getNotes(), 'acd_urls' => $accReq->getUrls(), 'acd_ip' => $accReq->getIP(), 'acd_filename' => $accReq->getFileName(), 'acd_storage_key' => $accReq->getFileStorageKey(), 'acd_areas' => $accReq->getAreas('flat'), 'acd_registration' => $dbw->timestamp($accReq->getRegistration()), 'acd_accepted' => $dbw->timestamp(), 'acd_user' => $this->admin->getID(), 'acd_comment' => $this->reason, 'acd_id' => $acd_id), __METHOD__);
         if (is_null($acd_id)) {
             $acd_id = $dbw->insertId();
             // set $acd_id to ID inserted
         }
     }
     # Add to global user login system (if there is one)
     if (!$wgAuth->addUser($user, $p, $accReq->getEmail(), $accReq->getRealName())) {
         $dbw->rollback();
         # DELETE new rows in case there was a COMMIT somewhere
         $this->acceptRequest_rollback($dbw, $user->getId(), $acd_id);
         return array('accountconf_externaldberror', wfMsgHtml('externaldberror'));
     }
     # OK, now remove the request from the queue
     $accReq->remove();
     # Commit this if we make past the CentralAuth system
     # and the groups are added. Next step is sending out an
     # email, which we cannot take back...
     $dbw->commit();
     # Prepare a temporary password email...
     if ($this->reason != '') {
         $msg = "confirmaccount-email-body2-pos{$this->type}";
         # If the user is in a group and there is a welcome for that group, use it
         if ($group && !wfEmptyMsg($msg)) {
             $ebody = wfMsgExt($msg, array('parsemag', 'content'), $user->getName(), $p, $this->reason);
             # Use standard if none found...
         } else {
             $ebody = wfMsgExt('confirmaccount-email-body2', array('parsemag', 'content'), $user->getName(), $p, $this->reason);
         }
     } else {
         $msg = "confirmaccount-email-body-pos{$this->type}";
         # If the user is in a group and there is a welcome for that group, use it
         if ($group && !wfEmptyMsg($msg)) {
             $ebody = wfMsgExt($msg, array('parsemag', 'content'), $user->getName(), $p, $this->reason);
             # Use standard if none found...
         } else {
             $ebody = wfMsgExt('confirmaccount-email-body', array('parsemag', 'content'), $user->getName(), $p, $this->reason);
         }
     }
     # Actually send out the email (@TODO: rollback on failure including $wgAuth)
     $result = $user->sendMail(wfMsgForContent('confirmaccount-email-subj'), $ebody);
     /*
     if ( !$result->isOk() ) {
     	# DELETE new rows in case there was a COMMIT somewhere
     	$this->acceptRequest_rollback( $dbw, $user->getId(), $acd_id );
     	return array( 'accountconf_mailerror',
     		wfMsg( 'mailerror', $context->getOutput()->parse( $result->getWikiText() ) ) );
     }
     */
     # Update user count
     $ssUpdate = new SiteStatsUpdate(0, 0, 0, 0, 1);
     $ssUpdate->doUpdate();
     # Safe to hook/log now...
     wfRunHooks('AddNewAccount', array($user, false));
     $user->addNewUserLogEntry();
     # Clear cache for notice of how many account requests there are
     ConfirmAccount::clearAccountRequestCountCache();
     # Delete any attached file and don't stop the whole process if this fails
     if ($wgAllowAccountRequestFiles) {
         $key = $accReq->getFileStorageKey();
         if ($key) {
             $repoOld = new FSRepo($wgConfirmAccountFSRepos['accountreqs']);
             $pathRel = UserAccountRequest::relPathFromKey($key);
             $oldPath = $repoOld->getZonePath('public') . '/' . $pathRel;
             if (file_exists($oldPath)) {
                 unlink($oldPath);
                 // delete!
             }
         }
     }
     # Start up the user's userpages if set to do so.
     # Will not append, so previous content will be blanked.
     $this->createUserPage($user);
     # Greet the new user if set to do so.
     $this->createUserTalkPage($user);
     return array(true, null);
 }
 /**
  * Attempt to validate and submit this data to the DB
  * @param $context IContextSource
  * @return array( true or error key string, html error msg or null )
  */
 public function submit(IContextSource $context)
 {
     global $wgAuth, $wgAccountRequestThrottle, $wgMemc, $wgContLang;
     global $wgConfirmAccountRequestFormItems;
     $formConfig = $wgConfirmAccountRequestFormItems;
     // convience
     $reqUser = $this->requester;
     # Make sure that basic permissions are checked
     $block = ConfirmAccount::getAccountRequestBlock($reqUser);
     if ($block) {
         return array('accountreq_permission_denied', $context->msg('badaccess-group0')->escaped());
     } elseif (wfReadOnly()) {
         return array('accountreq_readonly', $context->msg('badaccess-group0')->escaped());
     }
     # Now create a dummy user ($u) and check if it is valid
     if ($this->userName === '') {
         return array('accountreq_no_name', $context->msg('noname')->escaped());
     }
     $u = User::newFromName($this->userName, 'creatable');
     if (!$u) {
         return array('accountreq_invalid_name', $context->msg('noname')->escaped());
     }
     # No request spamming...
     if ($wgAccountRequestThrottle && $reqUser->isPingLimitable()) {
         $key = wfMemcKey('acctrequest', 'ip', $this->ip);
         $value = (int) $wgMemc->get($key);
         if ($value > $wgAccountRequestThrottle) {
             return array('accountreq_throttled', $context->msg('acct_request_throttle_hit', $wgAccountRequestThrottle)->text());
         }
     }
     # Make sure user agrees to policy here
     if ($formConfig['TermsOfService']['enabled'] && !$this->tosAccepted) {
         return array('acct_request_skipped_tos', $context->msg('requestaccount-agree')->escaped());
     }
     # Validate email address
     if (!Sanitizer::validateEmail($this->email)) {
         return array('acct_request_invalid_email', $context->msg('invalidemailaddress')->escaped());
     }
     # Check if biography is long enough
     if ($formConfig['Biography']['enabled'] && str_word_count($this->bio) < $formConfig['Biography']['minWords']) {
         $minWords = $formConfig['Biography']['minWords'];
         return array('acct_request_short_bio', $context->msg('requestaccount-tooshort')->numParams($minWords)->text());
     }
     # Per security reasons, file dir cannot be pulled from client,
     # so ask them to resubmit it then...
     # If the extra fields are off, then uploads are off
     $allowFiles = $formConfig['CV']['enabled'];
     if ($allowFiles && $this->attachmentPrevName && !$this->attachmentSrcName) {
         # If the user is submitting forgotAttachment as true with no file,
         # then they saw the notice and choose not to re-select the file.
         # Assume that they don't want to send one anymore.
         if (!$this->attachmentDidNotForget) {
             $this->attachmentPrevName = '';
             $this->attachmentDidNotForget = 0;
             return array(false, $context->msg('requestaccount-resub')->escaped());
         }
     }
     # Check if already in use
     if (0 != $u->idForName() || $wgAuth->userExists($u->getName())) {
         return array('accountreq_username_exists', $context->msg('userexists')->escaped());
     }
     # Set email and real name
     $u->setEmail($this->email);
     $u->setRealName($this->realName);
     $dbw = wfGetDB(DB_MASTER);
     $dbw->begin();
     // ready to acquire locks
     # Check pending accounts for name use
     if (!UserAccountRequest::acquireUsername($u->getName())) {
         $dbw->rollback();
         return array('accountreq_username_pending', $context->msg('requestaccount-inuse')->escaped());
     }
     # Check if someone else has an account request with the same email
     if (!UserAccountRequest::acquireEmail($u->getEmail())) {
         $dbw->rollback();
         return array('acct_request_email_exists', $context->msg('requestaccount-emaildup')->escaped());
     }
     # Process upload...
     if ($allowFiles && $this->attachmentSrcName) {
         global $wgAccountRequestExts, $wgConfirmAccountFSRepos;
         $ext = explode('.', $this->attachmentSrcName);
         $finalExt = $ext[count($ext) - 1];
         # File must have size.
         if (trim($this->attachmentSrcName) == '' || empty($this->attachmentSize)) {
             $this->attachmentPrevName = '';
             $dbw->rollback();
             return array('acct_request_empty_file', $context->msg('emptyfile')->escaped());
         }
         # Look at the contents of the file; if we can recognize the
         # type but it's corrupt or data of the wrong type, we should
         # probably not accept it.
         if (!in_array($finalExt, $wgAccountRequestExts)) {
             $this->attachmentPrevName = '';
             $dbw->rollback();
             return array('acct_request_bad_file_ext', $context->msg('requestaccount-exts')->escaped());
         }
         $veri = ConfirmAccount::verifyAttachment($this->attachmentTempPath, $finalExt);
         if (!$veri->isGood()) {
             $this->attachmentPrevName = '';
             $dbw->rollback();
             return array('acct_request_corrupt_file', $context->msg('verification-error')->escaped());
         }
         # Start a transaction, move file from temp to account request directory.
         $repo = new FSRepo($wgConfirmAccountFSRepos['accountreqs']);
         $key = sha1_file($this->attachmentTempPath) . '.' . $finalExt;
         $pathRel = UserAccountRequest::relPathFromKey($key);
         $triplet = array($this->attachmentTempPath, 'public', $pathRel);
         $status = $repo->storeBatch(array($triplet), FSRepo::OVERWRITE_SAME);
         // save!
         if (!$status->isOk()) {
             $dbw->rollback();
             return array('acct_request_file_store_error', $context->msg('filecopyerror', $this->attachmentTempPath, $pathRel)->escaped());
         }
     }
     $expires = null;
     // passed by reference
     $token = ConfirmAccount::getConfirmationToken($u, $expires);
     # Insert into pending requests...
     $req = UserAccountRequest::newFromArray(array('name' => $u->getName(), 'email' => $u->getEmail(), 'real_name' => $u->getRealName(), 'registration' => $this->registration, 'bio' => $this->bio, 'notes' => $this->notes, 'urls' => $this->urls, 'filename' => isset($this->attachmentSrcName) ? $this->attachmentSrcName : null, 'type' => $this->type, 'areas' => $this->areas, 'storage_key' => isset($key) ? $key : null, 'comment' => '', 'email_token' => md5($token), 'email_token_expires' => $expires, 'ip' => $this->ip, 'xff' => $this->xff, 'agent' => $this->agent));
     $req->insertOn();
     # Send confirmation, required!
     $result = ConfirmAccount::sendConfirmationMail($u, $this->ip, $token, $expires);
     if (!$result->isOK()) {
         $dbw->rollback();
         // nevermind
         if (isset($repo) && isset($pathRel)) {
             // remove attachment
             $repo->cleanupBatch(array(array('public', $pathRel)));
         }
         $param = $context->getOutput()->parse($result->getWikiText());
         return array('acct_request_mail_failed', $context->msg('mailerror')->rawParams($param)->escaped());
     }
     $dbw->commit();
     # Clear cache for notice of how many account requests there are
     ConfirmAccount::clearAccountRequestCountCache();
     # No request spamming...
     if ($wgAccountRequestThrottle && $reqUser->isPingLimitable()) {
         $ip = $context->getRequest()->getIP();
         $key = wfMemcKey('acctrequest', 'ip', $ip);
         $value = $wgMemc->incr($key);
         if (!$value) {
             $wgMemc->set($key, 1, 86400);
         }
     }
     # Done!
     return array(true, null);
 }
 /**
  * Show a private file requested by the visitor.
  * @param $key string
  * @return void
  */
 function showFile($key)
 {
     global $wgConfirmAccountFSRepos;
     $out = $this->getOutput();
     $request = $this->getRequest();
     $out->disable();
     # We mustn't allow the output to be Squid cached, otherwise
     # if an admin previews a private image, and it's cached, then
     # a user without appropriate permissions can toddle off and
     # nab the image, and Squid will serve it
     $request->response()->header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT');
     $request->response()->header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate');
     $request->response()->header('Pragma: no-cache');
     $repo = new FSRepo($wgConfirmAccountFSRepos['accountcreds']);
     $path = $repo->getZonePath('public') . '/' . UserAccountRequest::relPathFromKey($key);
     $repo->streamFile($path);
 }