Ejemplo n.º 1
0
 /**
  * Get a hash of a file's contents, either by retrieving a previously-
  * computed hash from the cache, or by computing a hash from the file.
  *
  * @private
  * @param string $filePath Full path to the file.
  * @param string $algo Name of selected hashing algorithm.
  * @return string|bool Hash of file contents, or false if the file could not be read.
  */
 public function getFileContentsHashInternal($filePath, $algo = 'md4')
 {
     $mtime = MediaWiki\quietCall('filemtime', $filePath);
     if ($mtime === false) {
         return false;
     }
     $cacheKey = wfGlobalCacheKey(__CLASS__, $filePath, $mtime, $algo);
     $hash = $this->cache->get($cacheKey);
     if ($hash) {
         return $hash;
     }
     $contents = MediaWiki\quietCall('file_get_contents', $filePath);
     if ($contents === false) {
         return false;
     }
     $hash = hash($algo, $contents);
     $this->cache->set($cacheKey, $hash, 60 * 60 * 24);
     // 24h
     return $hash;
 }
Ejemplo n.º 2
0
 public function getApproximateLagStatus()
 {
     if ($this->lagDetectionMethod === 'pt-heartbeat') {
         // Disable caching since this is fast enough and we don't wan't
         // to be *too* pessimistic by having both the cache TTL and the
         // pt-heartbeat interval count as lag in getSessionLagStatus()
         return parent::getApproximateLagStatus();
     }
     $key = wfGlobalCacheKey('mysql-lag', $this->getServer());
     $approxLag = $this->srvCache->get($key);
     if (!$approxLag) {
         $approxLag = parent::getApproximateLagStatus();
         $this->srvCache->set($key, $approxLag, 1);
     }
     return $approxLag;
 }
Ejemplo n.º 3
0
 /**
  * Run JavaScript or CSS data through a filter, caching the filtered result for future calls.
  *
  * Available filters are:
  *
  *    - minify-js \see JavaScriptMinifier::minify
  *    - minify-css \see CSSMin::minify
  *
  * If $data is empty, only contains whitespace or the filter was unknown,
  * $data is returned unmodified.
  *
  * @param string $filter Name of filter to run
  * @param string $data Text to filter, such as JavaScript or CSS text
  * @param array $options For back-compat, can also be the boolean value for "cacheReport". Keys:
  *  - (bool) cache: Whether to allow caching this data. Default: true.
  *  - (bool) cacheReport: Whether to include the "cache key" report comment. Default: false.
  * @return string Filtered data, or a comment containing an error message
  */
 public function filter($filter, $data, $options = array())
 {
     // Back-compat
     if (is_bool($options)) {
         $options = array('cacheReport' => $options);
     }
     // Defaults
     $options += array('cache' => true, 'cacheReport' => false);
     $stats = RequestContext::getMain()->getStats();
     // Don't filter empty content
     if (trim($data) === '') {
         return $data;
     }
     if (!in_array($filter, array('minify-js', 'minify-css'))) {
         $this->logger->warning('Invalid filter {filter}', array('filter' => $filter));
         return $data;
     }
     if (!$options['cache']) {
         $result = self::applyFilter($filter, $data, $this->config);
     } else {
         $key = wfGlobalCacheKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data));
         $cache = wfGetCache(wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING);
         $cacheEntry = $cache->get($key);
         if (is_string($cacheEntry)) {
             $stats->increment("resourceloader_cache.{$filter}.hit");
             return $cacheEntry;
         }
         $result = '';
         try {
             $statStart = microtime(true);
             $result = self::applyFilter($filter, $data, $this->config);
             $statTiming = microtime(true) - $statStart;
             $stats->increment("resourceloader_cache.{$filter}.miss");
             $stats->timing("resourceloader_cache.{$filter}.timing", 1000 * $statTiming);
             if ($options['cacheReport']) {
                 $result .= "\n/* cache key: {$key} */";
             }
             // Set a TTL since HHVM's APC doesn't have any limitation or eviction logic.
             $cache->set($key, $result, 24 * 3600);
         } catch (Exception $e) {
             MWExceptionHandler::logException($e);
             $this->logger->warning('Minification failed: {exception}', array('exception' => $e));
             $this->errors[] = self::formatExceptionNoComment($e);
         }
     }
     return $result;
 }
Ejemplo n.º 4
0
 /**
  * Run JavaScript or CSS data through a filter, caching the filtered result for future calls.
  *
  * Available filters are:
  *
  *    - minify-js \see JavaScriptMinifier::minify
  *    - minify-css \see CSSMin::minify
  *
  * If $data is empty, only contains whitespace or the filter was unknown,
  * $data is returned unmodified.
  *
  * @param string $filter Name of filter to run
  * @param string $data Text to filter, such as JavaScript or CSS text
  * @param array $options Keys:
  *  - (bool) cache: Whether to allow caching this data. Default: true.
  * @return string Filtered data, or a comment containing an error message
  */
 public static function filter($filter, $data, array $options = array())
 {
     if (strpos($data, ResourceLoader::FILTER_NOMIN) !== false) {
         return $data;
     }
     if (isset($options['cache']) && $options['cache'] === false) {
         return self::applyFilter($filter, $data);
     }
     $stats = RequestContext::getMain()->getStats();
     $cache = ObjectCache::newAccelerator(CACHE_ANYTHING);
     $key = wfGlobalCacheKey('resourceloader', 'filter', $filter, self::$filterCacheVersion, md5($data));
     $result = $cache->get($key);
     if ($result === false) {
         $stats->increment("resourceloader_cache.{$filter}.miss");
         $result = self::applyFilter($filter, $data);
         $cache->set($key, $result, 24 * 3600);
     } else {
         $stats->increment("resourceloader_cache.{$filter}.hit");
     }
     if ($result === null) {
         // Cached failure
         $result = $data;
     }
     return $result;
 }
Ejemplo n.º 5
0
 public function testWfGlobalCacheKey()
 {
     $this->setMwGlobals(array('wgCachePrefix' => 'ignored', 'wgDBname' => 'example', 'wgDBprefix' => ''));
     $one = wfGlobalCacheKey('some', 'thing');
     $this->assertEquals($one, 'global:some:thing');
     $this->setMwGlobals(array('wgDBname' => 'other', 'wgDBprefix' => 'mw_'));
     $two = wfGlobalCacheKey('some', 'thing');
     $this->assertEquals($one, $two, 'Not fragmented by wiki id');
 }
Ejemplo n.º 6
0
 /**
  * Clear the throttle counter.
  *
  * Should be called after a successful authentication attempt.
  *
  * @param string|null $username
  * @param string|null $ip
  * @throws \MWException
  */
 public function clear($username = null, $ip = null)
 {
     $userKey = $username ? md5($username) : null;
     foreach ($this->conditions as $index => $specificThrottle) {
         $ipKey = isset($specificThrottle['allIPs']) ? null : $ip;
         $throttleKey = wfGlobalCacheKey('throttler', $this->type, $index, $ipKey, $userKey);
         $this->cache->delete($throttleKey);
     }
 }
 /**
  * Construct a cache key for the results of a Pygments invocation.
  *
  * @param string $code Code to be highlighted.
  * @param string $lexer Lexer name.
  * @param array $options Options array.
  * @return string Cache key.
  */
 private static function makeCacheKey($code, $lexer, $options)
 {
     $optionString = FormatJson::encode($options, false, FormatJson::ALL_OK);
     $hash = md5("{$code}|{$lexer}|{$optionString}|" . self::CACHE_VERSION);
     if (function_exists('wfGlobalCacheKey')) {
         return wfGlobalCacheKey('highlight', $hash);
     } else {
         return 'highlight:' . $hash;
     }
 }
Ejemplo n.º 8
0
 /**
  * Make a new user account using the loaded data.
  * @private
  * @throws PermissionsError|ReadOnlyError
  * @return Status
  */
 public function addNewAccountInternal()
 {
     global $wgAuth, $wgMemc, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
     // If the user passes an invalid domain, something is fishy
     if (!$wgAuth->validDomain($this->mDomain)) {
         return Status::newFatal('wrongpassword');
     }
     // If we are not allowing users to login locally, we should be checking
     // to see if the user is actually able to authenticate to the authenti-
     // cation server before they create an account (otherwise, they can
     // create a local account and login as any domain user). We only need
     // to check this for domains that aren't local.
     if ('local' != $this->mDomain && $this->mDomain != '') {
         if (!$wgAuth->canCreateAccounts() && (!$wgAuth->userExists($this->mUsername) || !$wgAuth->authenticate($this->mUsername, $this->mPassword))) {
             return Status::newFatal('wrongpassword');
         }
     }
     if (wfReadOnly()) {
         throw new ReadOnlyError();
     }
     # Request forgery checks.
     if (!self::getCreateaccountToken()) {
         self::setCreateaccountToken();
         return Status::newFatal('nocookiesfornew');
     }
     # The user didn't pass a createaccount token
     if (!$this->mToken) {
         return Status::newFatal('sessionfailure');
     }
     # Validate the createaccount token
     if ($this->mToken !== self::getCreateaccountToken()) {
         return Status::newFatal('sessionfailure');
     }
     # Check permissions
     $currentUser = $this->getUser();
     $creationBlock = $currentUser->isBlockedFromCreateAccount();
     if (!$currentUser->isAllowed('createaccount')) {
         throw new PermissionsError('createaccount');
     } elseif ($creationBlock instanceof Block) {
         // Throws an ErrorPageError.
         $this->userBlockedMessage($creationBlock);
         // This should never be reached.
         return false;
     }
     # Include checks that will include GlobalBlocking (Bug 38333)
     $permErrors = $this->getPageTitle()->getUserPermissionsErrors('createaccount', $currentUser, true);
     if (count($permErrors)) {
         throw new PermissionsError('createaccount', $permErrors);
     }
     $ip = $this->getRequest()->getIP();
     if ($currentUser->isDnsBlacklisted($ip, true)) {
         return Status::newFatal('sorbs_create_account_reason');
     }
     # Now create a dummy user ($u) and check if it is valid
     $u = User::newFromName($this->mUsername, 'creatable');
     if (!$u) {
         return Status::newFatal('noname');
     }
     # Make sure the user does not exist already
     $lock = $wgMemc->getScopedLock(wfGlobalCacheKey('account', md5($this->mUsername)));
     if (!$lock) {
         return Status::newFatal('usernameinprogress');
     } elseif ($u->idForName(User::READ_LOCKING)) {
         return Status::newFatal('userexists');
     }
     if ($this->mCreateaccountMail) {
         # do not force a password for account creation by email
         # set invalid password, it will be replaced later by a random generated password
         $this->mPassword = null;
     } else {
         if ($this->mPassword !== $this->mRetype) {
             return Status::newFatal('badretype');
         }
         # check for password validity, return a fatal Status if invalid
         $validity = $u->checkPasswordValidity($this->mPassword, 'create');
         if (!$validity->isGood()) {
             $validity->ok = false;
             // make sure this Status is fatal
             return $validity;
         }
     }
     # if you need a confirmed email address to edit, then obviously you
     # need an email address.
     if ($wgEmailConfirmToEdit && strval($this->mEmail) === '') {
         return Status::newFatal('noemailtitle');
     }
     if (strval($this->mEmail) !== '' && !Sanitizer::validateEmail($this->mEmail)) {
         return Status::newFatal('invalidemailaddress');
     }
     # Set some additional data so the AbortNewAccount hook can be used for
     # more than just username validation
     $u->setEmail($this->mEmail);
     $u->setRealName($this->mRealName);
     $abortError = '';
     $abortStatus = null;
     if (!Hooks::run('AbortNewAccount', array($u, &$abortError, &$abortStatus))) {
         // Hook point to add extra creation throttles and blocks
         wfDebug("LoginForm::addNewAccountInternal: a hook blocked creation\n");
         if ($abortStatus === null) {
             // Report back the old string as a raw message status.
             // This will report the error back as 'createaccount-hook-aborted'
             // with the given string as the message.
             // To return a different error code, return a Status object.
             $abortError = new Message('createaccount-hook-aborted', array($abortError));
             $abortError->text();
             return Status::newFatal($abortError);
         } else {
             // For MediaWiki 1.23+ and updated hooks, return the Status object
             // returned from the hook.
             return $abortStatus;
         }
     }
     // Hook point to check for exempt from account creation throttle
     if (!Hooks::run('ExemptFromAccountCreationThrottle', array($ip))) {
         wfDebug("LoginForm::exemptFromAccountCreationThrottle: a hook " . "allowed account creation w/o throttle\n");
     } else {
         if ($wgAccountCreationThrottle && $currentUser->isPingLimitable()) {
             $key = wfMemcKey('acctcreate', 'ip', $ip);
             $value = $wgMemc->get($key);
             if (!$value) {
                 $wgMemc->set($key, 0, 86400);
             }
             if ($value >= $wgAccountCreationThrottle) {
                 return Status::newFatal('acct_creation_throttle_hit', $wgAccountCreationThrottle);
             }
             $wgMemc->incr($key);
         }
     }
     if (!$wgAuth->addUser($u, $this->mPassword, $this->mEmail, $this->mRealName)) {
         return Status::newFatal('externaldberror');
     }
     self::clearCreateaccountToken();
     return $this->initUser($u, false);
 }
Ejemplo n.º 9
0
 public function testWfGlobalCacheKey()
 {
     $cache = ObjectCache::getLocalClusterInstance();
     $this->assertEquals($cache->makeGlobalKey('foo', 123, 'bar'), wfGlobalCacheKey('foo', 123, 'bar'));
 }
Ejemplo n.º 10
0
 /**
  * Clear the login attempt throttle hit count for the (username,current IP) tuple.
  * @param string $username The user name
  * @return void
  */
 public static function clearLoginThrottle($username)
 {
     global $wgRequest, $wgPasswordAttemptThrottle;
     $canUsername = User::getCanonicalName($username, 'usable');
     $username = $canUsername !== false ? $canUsername : $username;
     if (is_array($wgPasswordAttemptThrottle)) {
         $throttleConfig = $wgPasswordAttemptThrottle;
         if (isset($wgPasswordAttemptThrottle['count'])) {
             // old style. Convert for backwards compat.
             $throttleConfig = [$wgPasswordAttemptThrottle];
         }
         foreach ($throttleConfig as $index => $specificThrottle) {
             if (isset($specificThrottle['allIPs'])) {
                 $ip = 'All';
             } else {
                 $ip = $wgRequest->getIP();
             }
             $throttleKey = wfGlobalCacheKey('password-throttle', $index, $ip, md5($username));
             ObjectCache::getLocalClusterInstance()->delete($throttleKey);
         }
     }
 }
 /**
  * Compute a non-cryptographic string hash of a file's contents.
  * If the file does not exist or cannot be read, returns an empty string.
  *
  * @since 1.26 Uses MD4 instead of SHA1.
  * @param string $filePath File path
  * @return string Hash
  */
 protected static function safeFileHash($filePath)
 {
     static $cache;
     if (!$cache) {
         $cache = ObjectCache::newAccelerator(CACHE_NONE);
     }
     MediaWiki\suppressWarnings();
     $mtime = filemtime($filePath);
     MediaWiki\restoreWarnings();
     if (!$mtime) {
         return '';
     }
     $cacheKey = wfGlobalCacheKey('resourceloader', __METHOD__, $filePath);
     $cachedHash = $cache->get($cacheKey);
     if (isset($cachedHash['mtime']) && $cachedHash['mtime'] === $mtime) {
         return $cachedHash['hash'];
     }
     MediaWiki\suppressWarnings();
     $contents = file_get_contents($filePath);
     MediaWiki\restoreWarnings();
     if (!$contents) {
         return '';
     }
     $hash = hash('md4', $contents);
     $cache->set($cacheKey, array('mtime' => $mtime, 'hash' => $hash), 60 * 60 * 24);
     return $hash;
 }
Ejemplo n.º 12
0
 private function getLagTimeCacheKey()
 {
     # Lag is per-server, not per-DB, so key on the master DB name
     return wfGlobalCacheKey('lag-times', $this->parent->getServerName(0));
 }
Ejemplo n.º 13
0
 /**
  * Compile a LESS file into CSS.
  *
  * Keeps track of all used files and adds them to localFileRefs.
  *
  * @since 1.22
  * @since 1.26 Added $context paramter.
  * @throws Exception If less.php encounters a parse error
  * @param string $fileName File path of LESS source
  * @param ResourceLoaderContext $context Context in which to generate script
  * @return string CSS source
  */
 protected function compileLessFile($fileName, ResourceLoaderContext $context)
 {
     static $cache;
     if (!$cache) {
         $cache = ObjectCache::newAccelerator(CACHE_ANYTHING);
     }
     // Construct a cache key from the LESS file name and a hash digest
     // of the LESS variables used for compilation.
     $vars = $this->getLessVars($context);
     ksort($vars);
     $varsHash = hash('md4', serialize($vars));
     $cacheKey = wfGlobalCacheKey('LESS', $fileName, $varsHash);
     $cachedCompile = $cache->get($cacheKey);
     // If we got a cached value, we have to validate it by getting a
     // checksum of all the files that were loaded by the parser and
     // ensuring it matches the cached entry's.
     if (isset($cachedCompile['hash'])) {
         $contentHash = FileContentsHasher::getFileContentsHash($cachedCompile['files']);
         if ($contentHash === $cachedCompile['hash']) {
             $this->localFileRefs = array_merge($this->localFileRefs, $cachedCompile['files']);
             return $cachedCompile['css'];
         }
     }
     $compiler = ResourceLoader::getLessCompiler($this->getConfig(), $vars);
     $css = $compiler->parseFile($fileName)->getCss();
     $files = $compiler->AllParsedFiles();
     $this->localFileRefs = array_merge($this->localFileRefs, $files);
     $cache->set($cacheKey, array('css' => $css, 'files' => $files, 'hash' => FileContentsHasher::getFileContentsHash($files)), 60 * 60 * 24);
     // 86400 seconds, or 24 hours.
     return $css;
 }
Ejemplo n.º 14
0
 function processLogin()
 {
     global $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle, $wgInvalidPasswordReset;
     $cache = ObjectCache::getLocalClusterInstance();
     $authRes = $this->authenticateUserData();
     switch ($authRes) {
         case self::SUCCESS:
             # We've verified now, update the real record
             $user = $this->getUser();
             $user->touch();
             if ($user->requiresHTTPS()) {
                 $this->mStickHTTPS = true;
             }
             if ($wgSecureLogin && !$this->mStickHTTPS) {
                 $user->setCookies($this->mRequest, false, $this->mRemember);
             } else {
                 $user->setCookies($this->mRequest, null, $this->mRemember);
             }
             self::clearLoginToken();
             // Reset the throttle
             $request = $this->getRequest();
             $key = wfGlobalCacheKey('password-throttle', $request->getIP(), md5($this->mUsername));
             $cache->delete($key);
             if ($this->hasSessionCookie() || $this->mSkipCookieCheck) {
                 /* Replace the language object to provide user interface in
                  * correct language immediately on this first page load.
                  */
                 $code = $request->getVal('uselang', $user->getOption('language'));
                 $userLang = Language::factory($code);
                 $wgLang = $userLang;
                 $this->getContext()->setLanguage($userLang);
                 // Reset SessionID on Successful login (bug 40995)
                 $this->renewSessionId();
                 if ($this->checkUserPasswordExpired($this->getUser()) == 'soft') {
                     $this->resetLoginForm($this->msg('resetpass-expired-soft'));
                 } elseif ($wgInvalidPasswordReset && !$user->isValidPassword($this->mPassword)) {
                     $status = $user->checkPasswordValidity($this->mPassword, 'login');
                     $this->resetLoginForm($status->getMessage('resetpass-validity-soft'));
                 } else {
                     $this->successfulLogin();
                 }
             } else {
                 $this->cookieRedirectCheck('login');
             }
             break;
         case self::NEED_TOKEN:
             $error = $this->mAbortLoginErrorMsg ?: 'nocookiesforlogin';
             $this->mainLoginForm($this->msg($error)->parse());
             break;
         case self::WRONG_TOKEN:
             $error = $this->mAbortLoginErrorMsg ?: 'sessionfailure';
             $this->mainLoginForm($this->msg($error)->text());
             break;
         case self::NO_NAME:
         case self::ILLEGAL:
             $error = $this->mAbortLoginErrorMsg ?: 'noname';
             $this->mainLoginForm($this->msg($error)->text());
             break;
         case self::WRONG_PLUGIN_PASS:
             $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
             $this->mainLoginForm($this->msg($error)->text());
             break;
         case self::NOT_EXISTS:
             if ($this->getUser()->isAllowed('createaccount')) {
                 $error = $this->mAbortLoginErrorMsg ?: 'nosuchuser';
                 $this->mainLoginForm($this->msg($error, wfEscapeWikiText($this->mUsername))->parse());
             } else {
                 $error = $this->mAbortLoginErrorMsg ?: 'nosuchusershort';
                 $this->mainLoginForm($this->msg($error, wfEscapeWikiText($this->mUsername))->text());
             }
             break;
         case self::WRONG_PASS:
             $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
             $this->mainLoginForm($this->msg($error)->text());
             break;
         case self::EMPTY_PASS:
             $error = $this->mAbortLoginErrorMsg ?: 'wrongpasswordempty';
             $this->mainLoginForm($this->msg($error)->text());
             break;
         case self::RESET_PASS:
             $error = $this->mAbortLoginErrorMsg ?: 'resetpass_announce';
             $this->resetLoginForm($this->msg($error));
             break;
         case self::CREATE_BLOCKED:
             $this->userBlockedMessage($this->getUser()->isBlockedFromCreateAccount());
             break;
         case self::THROTTLED:
             $error = $this->mAbortLoginErrorMsg ?: 'login-throttled';
             $this->mainLoginForm($this->msg($error)->params($this->getLanguage()->formatDuration($wgPasswordAttemptThrottle['seconds']))->text());
             break;
         case self::USER_BLOCKED:
             $error = $this->mAbortLoginErrorMsg ?: 'login-userblocked';
             $this->mainLoginForm($this->msg($error, $this->mUsername)->escaped());
             break;
         case self::ABORTED:
             $error = $this->mAbortLoginErrorMsg ?: 'login-abort-generic';
             $this->mainLoginForm($this->msg($error, wfEscapeWikiText($this->mUsername))->text());
             break;
         case self::USER_MIGRATED:
             $error = $this->mAbortLoginErrorMsg ?: 'login-migrated-generic';
             $params = array();
             if (is_array($error)) {
                 $error = array_shift($this->mAbortLoginErrorMsg);
                 $params = $this->mAbortLoginErrorMsg;
             }
             $this->mainLoginForm($this->msg($error, $params)->text());
             break;
         default:
             throw new MWException('Unhandled case value');
     }
     LoggerFactory::getInstance('authmanager')->info('Login attempt', array('event' => 'login', 'successful' => $authRes === self::SUCCESS, 'status' => LoginForm::$statusCodes[$authRes]));
 }
Ejemplo n.º 15
0
 /**
  * Load a library from the given file and execute it in the base environment.
  * @param string File name/path to load
  * @return mixed the export list, or null if there isn't one.
  */
 protected function loadLibraryFromFile($fileName)
 {
     static $cache = null;
     if (!$cache) {
         $cache = ObjectCache::newAccelerator(array(), 'hash');
     }
     $mtime = filemtime($fileName);
     if ($mtime === false) {
         throw new MWException('Lua file does not exist: ' . $fileName);
     }
     $cacheKey = wfGlobalCacheKey(__CLASS__, $fileName);
     $fileData = $cache->get($cacheKey);
     $code = false;
     if ($fileData) {
         list($code, $cachedMtime) = $fileData;
         if ($cachedMtime < $mtime) {
             $code = false;
         }
     }
     if (!$code) {
         $code = file_get_contents($fileName);
         if ($code === false) {
             throw new MWException('Lua file does not exist: ' . $fileName);
         }
         $cache->set($cacheKey, array($code, $mtime), 60 * 5);
     }
     # Prepending an "@" to the chunk name makes Lua think it is a filename
     $module = $this->getInterpreter()->loadString($code, '@' . basename($fileName));
     $ret = $this->getInterpreter()->callFunction($module);
     return isset($ret[0]) ? $ret[0] : null;
 }
 /**
  * Clear the login attempt throttle hit count for the (username,current IP) tuple.
  * @param string $username The user name
  * @return void
  */
 public static function clearLoginThrottle($username)
 {
     global $wgMemc, $wgRequest;
     $canUsername = User::getCanonicalName($username, 'usable');
     $username = $canUsername !== false ? $canUsername : $username;
     $throttleKey = wfGlobalCacheKey('password-throttle', $wgRequest->getIP(), md5($username));
     $wgMemc->delete($throttleKey);
 }