/**
  * Serialize and save data to memcache
  *
  * Note that it also sets a time to live for the
  * cached version set to self::TTL
  *
  * @param string $cacheKey Cache key
  * @param mixed  $data     Data to send to memcached, will use serialize();
  *
  * @return null
  */
 private function memcacheSave($data)
 {
     $url_friendly_key = MWCryptRand::generateHex(16);
     $key = wfMemcKey($url_friendly_key, 'wpdsso');
     $this->memcache->set($key, json_encode($data), self::TTL);
     return $url_friendly_key;
 }
 public function execute()
 {
     $user = $this->getUser();
     $params = $this->extractRequestParams();
     // If we're in JSON callback mode, no tokens can be obtained
     if ($this->lacksSameOriginSecurity()) {
         $this->dieUsage('Cannot obtain a centralauthtoken when using a callback', 'hascallback');
     }
     if ($user->isAnon()) {
         $this->dieUsage('Anonymous users cannot obtain a centralauthtoken', 'notloggedin');
     }
     if (CentralAuthHooks::hasApiToken()) {
         $this->dieUsage('Cannot obtain a centralauthtoken when using centralauthtoken', 'norecursion');
     }
     $centralUser = CentralAuthUser::getInstance($user);
     if (!$centralUser->exists() || !$centralUser->isAttached()) {
         $this->dieUsage('Cannot obtain a centralauthtoken without an attached global account', 'notattached');
     }
     $data = array('userName' => $user->getName(), 'token' => $centralUser->getAuthToken());
     global $wgMemc;
     $loginToken = MWCryptRand::generateHex(32) . dechex($centralUser->getId());
     $key = CentralAuthUser::memcKey('api-token', $loginToken);
     $wgMemc->add($key, $data, 60);
     $this->getResult()->addValue(null, $this->getModuleName(), array('centralauthtoken' => $loginToken));
 }
Ejemplo n.º 3
0
 public function crypt($password)
 {
     if (count($this->args) == 0) {
         $this->args[] = base64_encode(MWCryptRand::generate(16, true));
     }
     if (function_exists('hash_pbkdf2')) {
         $hash = hash_pbkdf2($this->params['algo'], $password, base64_decode($this->args[0]), (int) $this->params['rounds'], (int) $this->params['length'], true);
         if (!is_string($hash)) {
             throw new PasswordError('Error when hashing password.');
         }
     } else {
         $hashLenHash = hash($this->params['algo'], '', true);
         if (!is_string($hashLenHash)) {
             throw new PasswordError('Error when hashing password.');
         }
         $hashLen = strlen($hashLenHash);
         $blockCount = ceil($this->params['length'] / $hashLen);
         $hash = '';
         $salt = base64_decode($this->args[0]);
         for ($i = 1; $i <= $blockCount; ++$i) {
             $roundTotal = $lastRound = hash_hmac($this->params['algo'], $salt . pack('N', $i), $password, true);
             for ($j = 1; $j < $this->params['rounds']; ++$j) {
                 $lastRound = hash_hmac($this->params['algo'], $lastRound, $password, true);
                 $roundTotal ^= $lastRound;
             }
             $hash .= $roundTotal;
         }
         $hash = substr($hash, 0, $this->params['length']);
     }
     $this->hash = base64_encode($hash);
 }
Ejemplo n.º 4
0
 static function lqtThread($parser, $args, $parser, $frame)
 {
     $pout = $parser->getOutput();
     // Prepare information.
     $title = Title::newFromText($args['thread']);
     $thread = null;
     if ($args['thread']) {
         if (is_numeric($args['thread'])) {
             $thread = Threads::withId($args['thread']);
         } elseif ($title) {
             $article = new Article($title, 0);
             $thread = Threads::withRoot($article);
         }
     }
     if (is_null($thread)) {
         return '';
     }
     $data = array('type' => 'thread', 'args' => $args, 'thread' => $thread->id(), 'title' => $thread->title());
     if (!isset($pout->mLqtReplacements)) {
         $pout->mLqtReplacements = array();
     }
     // Generate a token
     $tok = MWCryptRand::generateHex(32);
     $text = '<!--LQT-THREAD-' . $tok . '-->';
     $pout->mLqtReplacements[$text] = $data;
     return $text;
 }
Ejemplo n.º 5
0
 public function crypt($plaintext)
 {
     if (count($this->args) == 0) {
         $this->args[] = MWCryptRand::generateHex(8);
     }
     $this->hash = md5($this->args[0] . '-' . md5($plaintext));
 }
Ejemplo n.º 6
0
 public function crypt($plaintext)
 {
     if (count($this->args) == 0) {
         $this->args[] = MWCryptRand::generateHex(8);
     }
     $this->hash = md5($this->args[0] . '-' . md5($plaintext));
     if (!is_string($this->hash) || strlen($this->hash) < 32) {
         throw new PasswordError('Error when hashing password.');
     }
 }
 protected function setUp()
 {
     // Needs to be before setup since this gets cached
     $this->mergeMwGlobalArrayValue('wgGroupPermissions', array('sysop' => array('deleterevision' => true)));
     parent::setUp();
     // Make a few edits for us to play with
     for ($i = 1; $i <= 5; $i++) {
         self::editPage(self::$page, MWCryptRand::generateHex(10), 'summary');
         $this->revs[] = Title::newFromText(self::$page)->getLatestRevID(Title::GAID_FOR_UPDATE);
     }
 }
 protected static function getPreloadId($create_if_not_exists)
 {
     global $wgUser, $wgRequest;
     if (!$wgUser->isAnon()) {
         return ModerationPreload::User_to_PreloadId($wgUser);
     }
     $anon_id = $wgRequest->getSessionData('anon_id');
     if (!$anon_id) {
         if (!$create_if_not_exists) {
             return false;
         }
         $anon_id = MWCryptRand::generateHex(32);
         $wgRequest->setSessionData('anon_id', $anon_id);
     }
     return ModerationPreload::AnonId_to_PreloadId($anon_id);
 }
 /**
  * Updates the underlying hash by encrypting it with the newest secret.
  *
  * @throws MWException If the configuration is not valid
  * @return bool True if the password was updated
  */
 public function update()
 {
     if (count($this->args) != 2 || $this->params == $this->getDefaultParams()) {
         // Hash does not need updating
         return false;
     }
     // Decrypt the underlying hash
     $underlyingHash = openssl_decrypt(base64_decode($this->args[1]), $this->params['cipher'], $this->config['secrets'][$this->params['secret']], 0, base64_decode($this->args[0]));
     // Reset the params
     $this->params = $this->getDefaultParams();
     // Check the key size with the new params
     $iv = MWCryptRand::generate(openssl_cipher_iv_length($this->params['cipher']), true);
     $this->hash = base64_encode(openssl_encrypt($underlyingHash, $this->params['cipher'], $this->config['secrets'][$this->params['secret']], 0, $iv));
     $this->args = array(base64_encode($iv));
     return true;
 }
Ejemplo n.º 10
0
 /**
  * @param string $password Password to encrypt
  *
  * @throws PasswordError If bcrypt has an unknown error
  * @throws MWException If bcrypt is not supported by PHP
  */
 public function crypt($password)
 {
     if (!defined('CRYPT_BLOWFISH')) {
         throw new MWException('Bcrypt is not supported.');
     }
     // Either use existing hash or make a new salt
     // Bcrypt expects 22 characters of base64-encoded salt
     // Note: bcrypt does not use MIME base64. It uses its own base64 without any '=' padding.
     //       It expects a 128 bit salt, so it will ignore anything after the first 128 bits
     if (!isset($this->args[0])) {
         $this->args[] = substr(strtr(base64_encode(MWCryptRand::generate(16, true)), '+', '.'), 0, 22);
     }
     $hash = crypt($password, sprintf('$2y$%02d$%s', (int) $this->params['rounds'], $this->args[0]));
     if (!is_string($hash) || strlen($hash) <= 13) {
         throw new PasswordError('Error when hashing password.');
     }
     // Strip the $2y$
     $parts = explode($this->getDelimiter(), substr($hash, 4));
     $this->params['rounds'] = (int) $parts[0];
     $this->args[0] = substr($parts[1], 0, 22);
     $this->hash = substr($parts[1], 22);
 }
Ejemplo n.º 11
0
 /**
  * Set a value in the session, encrypted
  *
  * This relies on the secrecy of $wgSecretKey (by default), or $wgSessionSecret.
  *
  * @param string|int $key
  * @param mixed $value
  */
 public function setSecret($key, $value)
 {
     global $wgSessionInsecureSecrets;
     list($encKey, $hmacKey) = $this->getSecretKeys();
     $serialized = serialize($value);
     // The code for encryption (with OpenSSL) and sealing is taken from
     // Chris Steipp's OATHAuthUtils class in Extension::OATHAuth.
     // Encrypt
     // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets
     $iv = \MWCryptRand::generate(16, true);
     if (function_exists('openssl_encrypt')) {
         $ciphertext = openssl_encrypt($serialized, 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv);
         if ($ciphertext === false) {
             throw new UnexpectedValueException('Encryption failed: ' . openssl_error_string());
         }
     } elseif (function_exists('mcrypt_encrypt')) {
         $ciphertext = mcrypt_encrypt('rijndael-128', $encKey, $serialized, 'ctr', $iv);
         if ($ciphertext === false) {
             throw new UnexpectedValueException('Encryption failed');
         }
     } elseif ($wgSessionInsecureSecrets) {
         $ex = new \Exception('No encryption is available, storing data as plain text');
         $this->logger->warning($ex->getMessage(), ['exception' => $ex]);
         $ciphertext = $serialized;
     } else {
         throw new \BadMethodCallException('Encryption is not available. You really should install the PHP OpenSSL extension, ' . 'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' . 'to accept insecure storage of sensitive session data, set ' . '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.');
     }
     // Seal
     $sealed = base64_encode($iv) . '.' . base64_encode($ciphertext);
     $hmac = hash_hmac('sha256', $sealed, $hmacKey, true);
     $encrypted = base64_encode($hmac) . '.' . $sealed;
     // Store
     $this->set($key, $encrypted);
 }
Ejemplo n.º 12
0
 /**
  * Generate, store, and return a new e-mail confirmation code.
  * A hash (unsalted, since it's used as a key) is stored.
  *
  * @note Call saveSettings() after calling this function to commit
  * this change to the database.
  *
  * @param string &$expiration Accepts the expiration time
  * @return string New token
  */
 protected function confirmationToken(&$expiration)
 {
     global $wgUserEmailConfirmationTokenExpiry;
     $now = time();
     $expires = $now + $wgUserEmailConfirmationTokenExpiry;
     $expiration = wfTimestamp(TS_MW, $expires);
     $this->load();
     $token = MWCryptRand::generateHex(32);
     $hash = md5($token);
     $this->mEmailToken = $hash;
     $this->mEmailTokenExpires = $expiration;
     return $token;
 }
Ejemplo n.º 13
0
 /**
  * Return a singleton instance of MWCryptRand
  * @return MWCryptRand
  */
 protected static function singleton()
 {
     if (is_null(self::$singleton)) {
         self::$singleton = new self();
     }
     return self::$singleton;
 }
Ejemplo n.º 14
0
 /**
  * Return an RFC4122 compliant v4 UUID
  *
  * @param int $flags Bitfield (supports UIDGenerator::QUICK_RAND)
  * @return string
  * @throws MWException
  */
 public static function newUUIDv4($flags = 0)
 {
     $hex = $flags & self::QUICK_RAND ? wfRandomString(31) : MWCryptRand::generateHex(31);
     return sprintf('%s-%s-%s-%s-%s', substr($hex, 0, 8), substr($hex, 8, 4), '4' . substr($hex, 12, 3), dechex(0x8 | hexdec($hex[15]) & 0x3) . $hex[16] . substr($hex, 17, 2), substr($hex, 19, 12));
 }
Ejemplo n.º 15
0
 /**
  * Handle redirection when the user needs to (re)authenticate.
  *
  * Send the user to the login form if needed; in case the request was a POST, stash in the
  * session and simulate it once the user gets back.
  *
  * @param string $subPage
  * @return bool False if execution should be stopped.
  * @throws ErrorPageError When the user is not allowed to use this page.
  */
 protected function handleReauthBeforeExecute($subPage)
 {
     $authManager = AuthManager::singleton();
     $request = $this->getRequest();
     $key = 'AuthManagerSpecialPage:reauth:' . $this->getName();
     $securityLevel = $this->getLoginSecurityLevel();
     if ($securityLevel) {
         $securityStatus = AuthManager::singleton()->securitySensitiveOperationStatus($securityLevel);
         if ($securityStatus === AuthManager::SEC_REAUTH) {
             $queryParams = array_diff_key($request->getQueryValues(), ['title' => true]);
             if ($request->wasPosted()) {
                 // unique ID in case the same special page is open in multiple browser tabs
                 $uniqueId = MWCryptRand::generateHex(6);
                 $key = $key . ':' . $uniqueId;
                 $queryParams = ['authUniqueId' => $uniqueId] + $queryParams;
                 $authData = array_diff_key($request->getValues(), $this->getPreservedParams(false), ['title' => 1]);
                 $authManager->setAuthenticationSessionData($key, $authData);
             }
             $title = SpecialPage::getTitleFor('Userlogin');
             $url = $title->getFullURL(['returnto' => $this->getFullTitle()->getPrefixedDBkey(), 'returntoquery' => wfArrayToCgi($queryParams), 'force' => $securityLevel], false, PROTO_HTTPS);
             $this->getOutput()->redirect($url);
             return false;
         } elseif ($securityStatus !== AuthManager::SEC_OK) {
             throw new ErrorPageError('cannotauth-not-allowed-title', 'cannotauth-not-allowed');
         }
     }
     $uniqueId = $request->getVal('authUniqueId');
     if ($uniqueId) {
         $key = $key . ':' . $uniqueId;
         $authData = $authManager->getAuthenticationSessionData($key);
         if ($authData) {
             $authManager->removeAuthenticationSessionData($key);
             $this->setRequest($authData, true);
         }
     }
     return true;
 }
Ejemplo n.º 16
0
 /**
  * Generate a random string suitable for a password
  *
  * @param int $minLength Minimum length of password to generate
  * @return string
  */
 public static function generateRandomPasswordString($minLength = 10)
 {
     // Decide the final password length based on our min password length,
     // stopping at a minimum of 10 chars.
     $length = max(10, $minLength);
     // Multiply by 1.25 to get the number of hex characters we need
     // Generate random hex chars
     $hex = MWCryptRand::generateHex(ceil($length * 1.25));
     // Convert from base 16 to base 32 to get a proper password like string
     return substr(Wikimedia\base_convert($hex, 16, 32, $length), -$length);
 }
Ejemplo n.º 17
0
 /**
  * Generate a secret value for variables using our CryptRand generator.
  * Produce a warning if the random source was insecure.
  *
  * @param $keys Array
  * @return Status
  */
 protected function doGenerateKeys($keys)
 {
     $status = Status::newGood();
     $strong = true;
     foreach ($keys as $name => $length) {
         $secretKey = MWCryptRand::generateHex($length, true);
         if (!MWCryptRand::wasStrong()) {
             $strong = false;
         }
         $this->setVar($name, $secretKey);
     }
     if (!$strong) {
         $names = array_keys($keys);
         $names = preg_replace('/^(.*)$/', '\\$$1', $names);
         global $wgLang;
         $status->warning('config-insecure-keys', $wgLang->listToText($names), count($names));
     }
     return $status;
 }
Ejemplo n.º 18
0
	/**
	 * Return an RFC4122 compliant v4 UUID
	 *
	 * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
	 * @return string
	 * @throws MWException
	 */
	public static function newUUIDv4( $flags = 0 ) {
		$hex = ( $flags & self::QUICK_RAND )
			? wfRandomString( 31 )
			: MWCryptRand::generateHex( 31 );

		return sprintf( '%s-%s-%s-%s-%s',
			// "time_low" (32 bits)
			substr( $hex, 0, 8 ),
			// "time_mid" (16 bits)
			substr( $hex, 8, 4 ),
			// "time_hi_and_version" (16 bits)
			'4' . substr( $hex, 12, 3 ),
			// "clk_seq_hi_res (8 bits, variant is binary 10x) and "clk_seq_low" (8 bits)
			dechex( 0x8 | ( hexdec( $hex[15] ) & 0x3 ) ) . $hex[16] . substr( $hex, 17, 2 ),
			// "node" (48 bits)
			substr( $hex, 19, 12 )
		);
	}
Ejemplo n.º 19
0
 /**
  * MW specific salt, cached from last run
  * @return string Binary string
  */
 protected function getSaltUsingCache()
 {
     if ($this->salt == '') {
         $lastSalt = $this->cache->get($this->cacheKey);
         if ($lastSalt === false) {
             // If we don't have a previous value to use as our salt, we use
             // 16 bytes from MWCryptRand, which will use a small amount of
             // entropy from our pool. Note, "XTR may be deterministic or keyed
             // via an optional “salt value”  (i.e., a non-secret random
             // value)..." - http://eprint.iacr.org/2010/264.pdf. However, we
             // use a strongly random value since we can.
             $lastSalt = MWCryptRand::generate(16);
         }
         // Get a binary string that is hashLen long
         $this->salt = hash($this->algorithm, $lastSalt, true);
     }
     return $this->salt;
 }
Ejemplo n.º 20
0
 /**
  * @return Title
  */
 function scratchTitle()
 {
     return Title::makeTitle(NS_LQT_THREAD, MWCryptRand::generateHex(32));
 }
Ejemplo n.º 21
0
 /**
  * Get a unique, stable identifier for this wiki
  *
  * If the identifier does not already exist, create it and save it in the
  * database. The identifier is randomly-generated.
  *
  * @return string 32-character hex string
  */
 private function getOrCreatePingbackId()
 {
     if (!$this->id) {
         $id = wfGetDB(DB_REPLICA)->selectField('updatelog', 'ul_value', ['ul_key' => 'PingBack']);
         if ($id == false) {
             $id = MWCryptRand::generateHex(32);
             $dbw = wfGetDB(DB_MASTER);
             $dbw->insert('updatelog', ['ul_key' => 'PingBack', 'ul_value' => $id], __METHOD__, 'IGNORE');
             if (!$dbw->affectedRows()) {
                 $id = $dbw->selectField('updatelog', 'ul_value', ['ul_key' => 'PingBack']);
             }
         }
         $this->id = $id;
     }
     return $this->id;
 }
Ejemplo n.º 22
0
/**
 * Override session_id before session startup if php's built-in
 * session generation code is not secure.
 */
function wfFixSessionID()
{
    // If the cookie or session id is already set we already have a session and should abort
    if (isset($_COOKIE[session_name()]) || session_id()) {
        return;
    }
    // PHP's built-in session entropy is enabled if:
    // - entropy_file is set or you're on Windows with php 5.3.3+
    // - AND entropy_length is > 0
    // We treat it as disabled if it doesn't have an entropy length of at least 32
    $entropyEnabled = (wfIsWindows() && version_compare(PHP_VERSION, '5.3.3', '>=') || ini_get('session.entropy_file')) && intval(ini_get('session.entropy_length')) >= 32;
    // If built-in entropy is not enabled or not sufficient override php's built in session id generation code
    if (!$entropyEnabled) {
        wfDebug(__METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n");
        session_id(MWCryptRand::generateHex(32));
    }
}
 /**
  * Gets a 32 character alphanumeric random string to be used for stats.
  * @return string
  */
 private static function getEditingStatsId()
 {
     if (self::$statsId) {
         return self::$statsId;
     }
     return self::$statsId = MWCryptRand::generateHex(32);
 }
Ejemplo n.º 24
0
/**
 * Reset the session_id
 *
 * @since 1.22
 */
function wfResetSessionID()
{
    global $wgCookieSecure;
    $oldSessionId = session_id();
    $cookieParams = session_get_cookie_params();
    if (wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure']) {
        session_regenerate_id(false);
    } else {
        $tmp = $_SESSION;
        session_destroy();
        wfSetupSession(MWCryptRand::generateHex(32));
        $_SESSION = $tmp;
    }
    $newSessionId = session_id();
    Hooks::run('ResetSessionID', array($oldSessionId, $newSessionId));
}
Ejemplo n.º 25
0
 /**
  * Make a new-style password hash
  *
  * @param $password String Plain-text password
  * @param bool|string $salt Optional salt, may be random or the user ID.
  *                     If unspecified or false, will generate one automatically
  * @return String Password hash
  */
 public static function crypt($password, $salt = false)
 {
     global $wgPasswordSalt;
     $hash = '';
     if (!wfRunHooks('UserCryptPassword', array(&$password, &$salt, &$wgPasswordSalt, &$hash))) {
         return $hash;
     }
     if ($wgPasswordSalt) {
         if ($salt === false) {
             $salt = MWCryptRand::generateHex(8);
         }
         return ':B:' . $salt . ':' . md5($salt . '-' . md5($password));
     } else {
         return ':A:' . md5($password);
     }
 }
Ejemplo n.º 26
0
 /**
  * Randomly generate a new createaccount token and attach it to the current session
  */
 public static function setCreateaccountToken()
 {
     global $wgRequest;
     $wgRequest->setSessionData('wsCreateaccountToken', MWCryptRand::generateHex(32));
 }
 /**
  * Create an API centralauth token
  * @return string|bool Token
  */
 static function getApiCentralAuthToken()
 {
     global $wgUser;
     if (!$wgUser->isAnon() && !self::hasApiToken()) {
         $centralUser = CentralAuthUser::getInstance($wgUser);
         if ($centralUser->exists() && $centralUser->isAttached()) {
             $data = array('userName' => $wgUser->getName(), 'token' => $centralUser->getAuthToken());
             global $wgMemc;
             $loginToken = MWCryptRand::generateHex(32) . dechex($centralUser->getId());
             $key = CentralAuthUser::memcKey('api-token', $loginToken);
             $wgMemc->add($key, $data, 60);
             return $loginToken;
         }
     }
     return false;
 }
Ejemplo n.º 28
0
 /**
  * Save the BotPassword to the database
  * @param string $operation 'update' or 'insert'
  * @param Password|null $password Password to set.
  * @return bool Success
  */
 public function save($operation, Password $password = null)
 {
     $conds = array('bp_user' => $this->centralId, 'bp_app_id' => $this->appId);
     $fields = array('bp_token' => MWCryptRand::generateHex(User::TOKEN_LENGTH), 'bp_restrictions' => $this->restrictions->toJson(), 'bp_grants' => FormatJson::encode($this->grants));
     if ($password !== null) {
         $fields['bp_password'] = $password->toString();
     } elseif ($operation === 'insert') {
         $fields['bp_password'] = PasswordFactory::newInvalidPassword()->toString();
     }
     $dbw = self::getDB(DB_MASTER);
     switch ($operation) {
         case 'insert':
             $dbw->insert('bot_passwords', $fields + $conds, __METHOD__, array('IGNORE'));
             break;
         case 'update':
             $dbw->update('bot_passwords', $fields, $conds, __METHOD__);
             break;
         default:
             return false;
     }
     $ok = (bool) $dbw->affectedRows();
     if ($ok) {
         $this->token = $dbw->selectField('bot_passwords', 'bp_token', $conds, __METHOD__);
         $this->isSaved = true;
     }
     return $ok;
 }
Ejemplo n.º 29
0
 /**
  * Generate a new random session ID
  * @return string
  */
 public function generateSessionId()
 {
     do {
         $id = wfBaseConvert(\MWCryptRand::generateHex(40), 16, 32, 32);
         $key = wfMemcKey('MWSession', $id);
     } while (isset($this->allSessionIds[$id]) || is_array($this->store->get($key)));
     return $id;
 }
Ejemplo n.º 30
0
 /**
  * Renew the user's session id, using strong entropy
  */
 private function renewSessionId()
 {
     global $wgSecureLogin, $wgCookieSecure;
     if ($wgSecureLogin && !$this->mStickHTTPS) {
         $wgCookieSecure = false;
     }
     // If either we don't trust PHP's entropy, or if we need
     // to change cookie settings when logging in because of
     // wpStickHTTPS, then change the session ID manually.
     $cookieParams = session_get_cookie_params();
     if (wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure']) {
         session_regenerate_id(false);
     } else {
         $tmp = $_SESSION;
         session_destroy();
         wfSetupSession(MWCryptRand::generateHex(32));
         $_SESSION = $tmp;
     }
 }