Added Allow/Disallow multiple identities

This commit is contained in:
RainLoop Team 2015-05-18 00:38:20 +04:00
parent 28d93f70ae
commit 2f482a6288
49 changed files with 32740 additions and 32243 deletions

View file

@ -194,9 +194,8 @@ class Actions
*/
public function GetShortLifeSpecAuthToken($iLife = 60)
{
$sToken = $this->getAuthToken();
$aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken);
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && is_array($aAccountHash))
$aAccountHash = \RainLoop\Utils::DecodeKeyValues($this->getLocalAuthToken());
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && \is_array($aAccountHash))
{
$aAccountHash[10] = \time() + $iLife;
return \RainLoop\Utils::EncodeKeyValues($aAccountHash);
@ -458,6 +457,18 @@ class Actions
$this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine);
}
if (false !== \strpos($sLine, '{request:domain}'))
{
$sLine = \str_replace('{request:domain}',
$this->Http()->GetHost(false, true, true), $sLine);
}
if (false !== \strpos($sLine, '{request:domain-clear}'))
{
$sLine = \str_replace('{request:domain-clear}',
\MailSo\Base\Utils::GetClearDomainName($this->Http()->GetHost(false, true, true)), $sLine);
}
$sLine = \preg_replace('/\{request:([^}]*)\}/i', 'request', $sLine);
}
@ -489,8 +500,12 @@ class Actions
{
$sEmail = $oAccount->Email();
$sLine = \str_replace('{user:email}', $sEmail, $sLine);
$sLine = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine);
$sLine = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine);
$sLine = \str_replace('{user:login}',
\MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine);
$sLine = \str_replace('{user:domain}',
\MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine);
$sLine = \str_replace('{user:domain-clear}',
\MailSo\Base\Utils::GetClearDomainName(\MailSo\Base\Utils::GetDomainFromEmail($sEmail)), $sLine);
}
}
@ -552,7 +567,7 @@ class Actions
{
if ($oAccount)
{
$sSpecAuthToken = '_'.$oAccount->GetAuthToken();
$sSpecAuthToken = '_'.$oAccount->GetAuthTokenQ();
$this->SetSpecAuthToken($sSpecAuthToken);
\RainLoop\Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken, 0, '/', null, null, true);
@ -560,7 +575,7 @@ class Actions
if ($oAccount->SignMe() && 0 < \strlen($oAccount->SignMeToken()))
{
\RainLoop\Utils::SetCookie(self::AUTH_SIGN_ME_TOKEN_KEY,
\RainLoop\Utils::EncodeKeyValues(array(
\RainLoop\Utils::EncodeKeyValuesQ(array(
'e' => $oAccount->Email(),
't' => $oAccount->SignMeToken()
)),
@ -569,9 +584,9 @@ class Actions
$this->StorageProvider()->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'sign_me',
\RainLoop\Utils::EncodeKeyValues(array(
\RainLoop\Utils::EncodeKeyValuesQ(array(
'Time' => \time(),
'AuthToken' => $oAccount->GetAuthToken(),
'AuthToken' => $oAccount->GetAuthTokenQ(),
'SignMetToken' => $oAccount->SignMeToken()
))
);
@ -618,7 +633,7 @@ class Actions
/**
* @return string
*/
private function getAuthToken()
private function getLocalAuthToken()
{
$sToken = $this->GetSpecAuthToken();
return !empty($sToken) && '_' === \substr($sToken, 0, 1) ? \substr($sToken, 1) : '';
@ -637,7 +652,7 @@ class Actions
*/
public function ClearAdminAuthToken()
{
$aAdminHash = \RainLoop\Utils::DecodeKeyValues($this->getAdminAuthToken());
$aAdminHash = \RainLoop\Utils::DecodeKeyValuesQ($this->getAdminAuthToken());
if (
!empty($aAdminHash[0]) && !empty($aAdminHash[1]) && !empty($aAdminHash[2]) &&
'token' === $aAdminHash[0] && \md5(APP_SALT) === $aAdminHash[1]
@ -1085,7 +1100,7 @@ class Actions
$sRand = '';
}
return '' === $sRand ? '' : \RainLoop\Utils::EncodeKeyValues(array('token', \md5(APP_SALT), $sRand));
return '' === $sRand ? '' : \RainLoop\Utils::EncodeKeyValuesQ(array('token', \md5(APP_SALT), $sRand));
}
/**
@ -1098,7 +1113,7 @@ class Actions
$bResult = false;
if ($this->Config()->Get('security', 'allow_admin_panel', true))
{
$aAdminHash = \RainLoop\Utils::DecodeKeyValues($this->getAdminAuthToken());
$aAdminHash = \RainLoop\Utils::DecodeKeyValuesQ($this->getAdminAuthToken());
if (!empty($aAdminHash[0]) && !empty($aAdminHash[1]) && !empty($aAdminHash[2]) &&
'token' === $aAdminHash[0] && \md5(APP_SALT) === $aAdminHash[1] &&
'' !== $this->Cacher(null, true)->Get(\RainLoop\KeyPathHelper::SessionAdminKey($aAdminHash[2]), '')
@ -1124,7 +1139,7 @@ class Actions
if (!empty($sTo))
{
\RainLoop\Utils::SetCookie(self::AUTH_MAILTO_TOKEN_KEY,
\RainLoop\Utils::EncodeKeyValues(array(
\RainLoop\Utils::EncodeKeyValuesQ(array(
'Time' => \microtime(true),
'MailTo' => 'MailTo',
'To' => $sTo
@ -1177,16 +1192,17 @@ class Actions
* @param string $sToken
* @param bool $bThrowExceptionOnFalse = true
* @param bool $bValidateShortToken = true
* @param bool $bQ = false
*
* @return \RainLoop\Model\Account|bool
* @throws \RainLoop\Exceptions\ClientException
*/
public function GetAccountFromCustomToken($sToken, $bThrowExceptionOnFalse = true, $bValidateShortToken = true)
public function GetAccountFromCustomToken($sToken, $bThrowExceptionOnFalse = true, $bValidateShortToken = true, $bQ = false)
{
$oResult = false;
if (!empty($sToken))
{
$aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken);
$aAccountHash = $bQ ? \RainLoop\Utils::DecodeKeyValuesQ($sToken) : \RainLoop\Utils::DecodeKeyValues($sToken);
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && // simple token validation
8 <= \count($aAccountHash) && // length checking
!empty($aAccountHash[7]) && // does short token exist
@ -1236,7 +1252,7 @@ class Actions
$sSignMeToken = \RainLoop\Utils::GetCookie(\RainLoop\Actions::AUTH_SIGN_ME_TOKEN_KEY, '');
if (!empty($sSignMeToken))
{
$aTokenData = \RainLoop\Utils::DecodeKeyValues($sSignMeToken);
$aTokenData = \RainLoop\Utils::DecodeKeyValuesQ($sSignMeToken);
if (\is_array($aTokenData) && !empty($aTokenData['e']) && !empty($aTokenData['t']))
{
$sTokenSettings = $this->StorageProvider()->Get($aTokenData['e'],
@ -1246,13 +1262,13 @@ class Actions
if (!empty($sTokenSettings))
{
$aSignMeData = \RainLoop\Utils::DecodeKeyValues($sTokenSettings);
$aSignMeData = \RainLoop\Utils::DecodeKeyValuesQ($sTokenSettings);
if (\is_array($aSignMeData) &&
!empty($aSignMeData['AuthToken']) &&
!empty($aSignMeData['SignMetToken']) &&
$aSignMeData['SignMetToken'] === $aTokenData['t'])
{
$oAccount = $this->GetAccountFromCustomToken($aSignMeData['AuthToken'], false, false);
$oAccount = $this->GetAccountFromCustomToken($aSignMeData['AuthToken'], false, false, true);
}
}
}
@ -1273,7 +1289,7 @@ class Actions
*/
public function getAccountFromToken($bThrowExceptionOnFalse = true)
{
return $this->GetAccountFromCustomToken($this->getAuthToken(), $bThrowExceptionOnFalse);
return $this->GetAccountFromCustomToken($this->getLocalAuthToken(), $bThrowExceptionOnFalse, true, true);
}
/**
@ -1467,9 +1483,10 @@ class Actions
if (null !== $sToken)
{
\RainLoop\Utils::ClearCookie(self::AUTH_MAILTO_TOKEN_KEY);
$mMailToData = \RainLoop\Utils::DecodeKeyValues($sToken);
if (\is_array($mMailToData) && !empty($mMailToData['MailTo']) && 'MailTo' === $mMailToData['MailTo'] &&
!empty($mMailToData['To']))
$mMailToData = \RainLoop\Utils::DecodeKeyValuesQ($sToken);
if (\is_array($mMailToData) && !empty($mMailToData['MailTo']) &&
'MailTo' === $mMailToData['MailTo'] && !empty($mMailToData['To']))
{
$aResult['MailToEmail'] = $mMailToData['To'];
}
@ -1496,6 +1513,8 @@ class Actions
$aResult['WelcomePageUrl'] = '';
$aResult['WelcomePageDisplay'] = '';
}
$aResult['StartupUrl'] = $this->compileLogParams($aResult['StartupUrl'], $oAccount);
}
else
{
@ -1509,6 +1528,8 @@ class Actions
$aResult['WelcomePageUrl'] = '';
$aResult['WelcomePageDisplay'] = '';
$aResult['StartupUrl'] = '';
}
$aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false);
@ -2109,51 +2130,27 @@ class Actions
*/
private function clientRsaDecryptHelper($sEncryptedData)
{
$aMatch = array();
if ('rsa:xxx:' === substr($sEncryptedData, 0, 8) && $this->Config()->Get('security', 'use_rsa_encryption', false))
{
$oLogger = $this->Logger();
$oLogger->Write('Trying to decode encrypted data', \MailSo\Log\Enumerations\Type::INFO, 'RSA');
$sPrivateKey = file_exists(APP_PRIVATE_DATA.'rsa/private') ?
\file_get_contents(APP_PRIVATE_DATA.'rsa/private') : '';
if (!empty($sPrivateKey))
{
$sData = \trim(\substr($sEncryptedData, 8));
if (!\class_exists('Crypt_RSA'))
{
\set_include_path(\get_include_path().PATH_SEPARATOR.APP_VERSION_ROOT_PATH.'app/libraries/phpseclib');
include_once 'Crypt/RSA.php';
\defined('CRYPT_RSA_MODE') || \define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
}
$oLogger->HideErrorNotices(true);
$oRsa = new \Crypt_RSA();
$oRsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$oRsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$oRsa->setPrivateKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$oRsa->loadKey($sPrivateKey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$sData = $oRsa->decrypt(\base64_decode($sData));
if (\preg_match('/^[a-z0-9]{32}:(.+):[a-z0-9]{32}$/', $sData, $aMatch) && isset($aMatch[1]))
{
$sEncryptedData = $aMatch[1];
}
else
{
$oLogger->Write('Invalid decrypted data', \MailSo\Log\Enumerations\Type::WARNING, 'RSA');
}
$oLogger->HideErrorNotices(false);
}
else
{
$oLogger->Write('Private key is not found', \MailSo\Log\Enumerations\Type::WARNING, 'RSA');
}
}
// $aMatch = array();
// if ('rsa:xxx:' === \substr($sEncryptedData, 0, 8) && $this->Config()->Get('security', 'use_rsa_encryption', false))
// {
// $oLogger = $this->Logger();
// $oLogger->Write('Trying to decode encrypted data', \MailSo\Log\Enumerations\Type::INFO, 'RSA');
// $oLogger->HideErrorNotices(true);
//
// $sData = \trim(\substr($sEncryptedData, 8));
// $sData = \RainLoop\Utils::DecryptStringRSA(\base64_decode($sData));
//
// if (false !== $sData && \preg_match('/^[a-z0-9]{32}:(.+):[a-z0-9]{32}$/', $sData, $aMatch) && isset($aMatch[1]))
// {
// $sEncryptedData = $aMatch[1];
// }
// else
// {
// $oLogger->Write('Invalid decrypted data', \MailSo\Log\Enumerations\Type::WARNING, 'RSA');
// }
//
// $oLogger->HideErrorNotices(false);
// }
return $sEncryptedData;
}
@ -2290,6 +2287,7 @@ class Actions
1 < \count($aOrder['Accounts']))
{
$aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts);
$aAccounts = \array_filter($aAccounts, function ($sHash) {
return 5 < \strlen($sHash);
});
@ -2390,10 +2388,11 @@ class Actions
/**
* @param \RainLoop\Model\Account $oAccount
* @param bool $bAllowIdentities = false
*
* @return array
*/
public function GetIdentities($oAccount)
public function GetIdentities($oAccount, $bAllowIdentities = false)
{
$aIdentities = array();
if ($oAccount)
@ -2421,14 +2420,14 @@ class Actions
if ($oItem && $oItem->Validate())
{
if ('' === $oItem->Id())
if ($oItem->IsAccountIdentities())
{
$oItem->SetEmail($oAccount->Email());
$bHasAccountIdentity = true;
\array_unshift($aIdentities, $oItem);
\array_push($aIdentities, $oItem);
}
else
else if ($bAllowIdentities)
{
\array_push($aIdentities, $oItem);
}
@ -2442,7 +2441,7 @@ class Actions
\RainLoop\Model\Identity::NewInstanceFromAccount($oAccount));
}
if (1 < \count($aIdentities))
if (1 < \count($aIdentities) && $bAllowIdentities)
{
$sOrder = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
@ -2454,8 +2453,16 @@ class Actions
1 < \count($aOrder['Identities']))
{
$aList = $aOrder['Identities'];
foreach ($aList as $iIndex => $sItem)
{
if ('' === $sItem)
{
$aList[$iIndex] = '---';
}
}
\usort($aIdentities, function ($a, $b) use ($aList) {
return \array_search($a->Id(), $aList) < \array_search($b->Id(), $aList) ? -1 : 1;
return \array_search($a->Id(true), $aList) < \array_search($b->Id(true), $aList) ? -1 : 1;
});
}
}
@ -2527,14 +2534,20 @@ class Actions
/**
* @param \RainLoop\Model\Account $oAccount
* @param array $aIdentities = array()
* @param bool $bAllowIdentities = false
*
* @return array
*/
public function SetIdentities($oAccount, $aIdentities = array())
public function SetIdentities($oAccount, $aIdentities = array(), $bAllowIdentities = false)
{
$aResult = array();
foreach ($aIdentities as $oItem)
{
if (!$bAllowIdentities && $oItem && !$oItem->IsAccountIdentities())
{
continue;
}
$aResult[] = $oItem->ToSimpleJSON(false);
}
@ -2675,7 +2688,7 @@ class Actions
$oNewAccount->SetParentEmail($sParentEmail);
$aAccounts[$oNewAccount->Email()] = $oNewAccount->GetAuthToken();
if (0 === \strlen($oAccount->ParentEmail()))
if (!$oAccount->IsAdditionalAccount())
{
$aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken();
}
@ -2824,7 +2837,7 @@ class Actions
$mResult = array(
'Files' => array(array(
'FileName' => 'attachments.zip',
'Hash' => \RainLoop\Utils::EncodeKeyValues(array(
'Hash' => \RainLoop\Utils::EncodeKeyValuesQ(array(
'V' => APP_VERSION,
'Account' => $oAccount ? \md5($oAccount->Hash()) : '',
'FileName' => 'attachments.zip',
@ -2912,7 +2925,7 @@ class Actions
{
$mResult['Files'][] = array(
'FileName' => isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat',
'Hash' => \RainLoop\Utils::EncodeKeyValues($aItem)
'Hash' => \RainLoop\Utils::EncodeKeyValuesQ($aItem)
);
}
@ -2943,20 +2956,34 @@ class Actions
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::InvalidInputArgument);
}
$aIdentitiesForSave = array();
$aIdentities = $this->GetIdentities($oAccount,
$this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount));
$aIdentities = $this->GetIdentities($oAccount);
$bAdded = false;
$aIdentitiesForSave = array();
foreach ($aIdentities as $oItem)
{
if ($oItem && $oItem->Id() !== $oIdentity->Id())
if ($oItem)
{
$aIdentitiesForSave[] = $oItem;
if ($oItem->Id() === $oIdentity->Id())
{
$aIdentitiesForSave[] = $oIdentity;
$bAdded = true;
}
else
{
$aIdentitiesForSave[] = $oItem;
}
}
}
$aIdentitiesForSave[] = $oIdentity;
if (!$bAdded)
{
$aIdentitiesForSave[] = $oIdentity;
}
return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aIdentitiesForSave));
return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aIdentitiesForSave,
$this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount)));
}
/**
@ -2968,6 +2995,11 @@ class Actions
{
$oAccount = $this->getAccountFromToken();
if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
$sId = \trim($this->GetActionParam('IdToDelete', ''));
if (empty($sId))
{
@ -2975,7 +3007,7 @@ class Actions
}
$aNew = array();
$aIdentities = $this->GetIdentities($oAccount);
$aIdentities = $this->GetIdentities($oAccount, $this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount));
foreach ($aIdentities as $oItem)
{
if ($oItem && $sId !== $oItem->Id())
@ -2984,7 +3016,8 @@ class Actions
}
}
return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aNew));
return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aNew,
$this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount)));
}
/**
@ -3110,7 +3143,7 @@ class Actions
$aAccounts = $this->GetActionParam('Accounts', null);
$aIdentities = $this->GetActionParam('Identities', null);
if (!\is_array($aAccounts) || !\is_array($aIdentities))
if (!\is_array($aAccounts) && !\is_array($aIdentities))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -3118,8 +3151,8 @@ class Actions
return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order',
\json_encode(array(
'Accounts' => $aAccounts,
'Identities' => $aIdentities
'Accounts' => \is_array($aAccounts) ? $aAccounts : array(),
'Identities' => \is_array($aIdentities) ? $aIdentities : array()
))
));
}
@ -3134,6 +3167,7 @@ class Actions
$oAccount = $this->getAccountFromToken();
$mAccounts = false;
if ($this->GetCapa(false, \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount))
{
$mAccounts = $this->GetAccounts($oAccount);
@ -3147,7 +3181,8 @@ class Actions
return $this->DefaultResponse(__FUNCTION__, array(
'Accounts' => $mAccounts,
'Identities' => $this->GetIdentities($oAccount)
'Identities' => $this->GetIdentities($oAccount,
$this->GetCapa(false, \RainLoop\Enumerations\Capa::IDENTITIES, $oAccount))
));
}
@ -3448,6 +3483,9 @@ class Actions
case \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS:
$this->setConfigFromParams($oConfig, $sParamName, 'webmail', 'allow_additional_accounts', 'bool');
break;
case \RainLoop\Enumerations\Capa::IDENTITIES:
$this->setConfigFromParams($oConfig, $sParamName, 'webmail', 'allow_additional_identities', 'bool');
break;
case \RainLoop\Enumerations\Capa::TEMPLATES:
$this->setConfigFromParams($oConfig, $sParamName, 'capa', 'templates', 'bool');
break;
@ -3560,6 +3598,7 @@ class Actions
});
$this->setCapaFromParams($oConfig, 'CapaAdditionalAccounts', \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS);
$this->setCapaFromParams($oConfig, 'CapaIdentities', \RainLoop\Enumerations\Capa::IDENTITIES);
$this->setCapaFromParams($oConfig, 'CapaTemplates', \RainLoop\Enumerations\Capa::TEMPLATES);
$this->setCapaFromParams($oConfig, 'CapaTwoFactorAuth', \RainLoop\Enumerations\Capa::TWO_FACTOR);
$this->setCapaFromParams($oConfig, 'CapaTwoFactorAuthForce', \RainLoop\Enumerations\Capa::TWO_FACTOR_FORCE);
@ -7128,19 +7167,19 @@ class Actions
try
{
$aAttachments = $this->GetActionParam('Attachments', array());
if (is_array($aAttachments) && 0 < count($aAttachments))
if (\is_array($aAttachments) && 0 < \count($aAttachments))
{
$mResult = array();
foreach ($aAttachments as $sAttachment)
{
$aValues = \RainLoop\Utils::DecodeKeyValues($sAttachment);
if (is_array($aValues))
$aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment);
if (\is_array($aValues))
{
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
$iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0;
$sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '';
$sTempName = md5($sAttachment);
$sTempName = \md5($sAttachment);
if (!$this->FilesProvider()->FileExists($oAccount, $sTempName))
{
$this->MailClient()->MessageMimeStream(
@ -7738,7 +7777,7 @@ class Actions
$aParams = $this->GetActionParam('Params', null);
$this->Http()->ServerNoCache();
$aData = \RainLoop\Utils::DecodeKeyValues($sRawKey);
$aData = \RainLoop\Utils::DecodeKeyValuesQ($sRawKey);
if (isset($aParams[0], $aParams[1], $aParams[2]) &&
'Raw' === $aParams[0] && 'FramedView' === $aParams[2] && isset($aData['Framed']) && $aData['Framed'] && $aData['FileName'])
{
@ -7842,6 +7881,11 @@ class Actions
$aResult[] = \RainLoop\Enumerations\Capa::ADDITIONAL_ACCOUNTS;
}
if ($oConfig->Get('webmail', 'allow_additional_identities', false))
{
$aResult[] = \RainLoop\Enumerations\Capa::IDENTITIES;
}
if ($oConfig->Get('security', 'allow_two_factor_auth', false) &&
($bAdmin || ($oAccount && !$oAccount->IsAdditionalAccount())))
{
@ -8399,7 +8443,7 @@ class Actions
$bResult = false;
if (!empty($sRawKey))
{
$aValues = \RainLoop\Utils::DecodeKeyValues($sRawKey);
$aValues = \RainLoop\Utils::DecodeKeyValuesQ($sRawKey);
if (is_array($aValues))
{
$bResult = $aValues;
@ -9205,7 +9249,7 @@ class Actions
$sSubject = $mResult['Subject'];
$mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']);
$mResult['RequestHash'] = \RainLoop\Utils::EncodeKeyValues(array(
$mResult['RequestHash'] = \RainLoop\Utils::EncodeKeyValuesQ(array(
'V' => APP_VERSION,
'Account' => $oAccount ? \md5($oAccount->Hash()) : '',
'Folder' => $mResult['Folder'],
@ -9270,7 +9314,7 @@ class Actions
if (!!$this->Config()->Get('labs', 'use_local_proxy_for_external_images', false))
{
$fAdditionalExternalFilter = function ($sUrl) {
return './?/ProxyExternal/'.\RainLoop\Utils::EncodeKeyValues(array(
return './?/ProxyExternal/'.\RainLoop\Utils::EncodeKeyValuesQ(array(
'Rnd' => \md5(\microtime(true)),
'Token' => \RainLoop\Utils::GetConnectionToken(),
'Url' => $sUrl
@ -9421,7 +9465,7 @@ class Actions
$mResult['IsThumbnail'] = $this->isFileHasThumbnail($mResult['FileName']);
}
$mResult['Download'] = \RainLoop\Utils::EncodeKeyValues(array(
$mResult['Download'] = \RainLoop\Utils::EncodeKeyValuesQ(array(
'V' => APP_VERSION,
'Account' => $oAccount ? \md5($oAccount->Hash()) : '',
'Folder' => $mResult['Folder'],

View file

@ -139,12 +139,13 @@ class Api
{
$sSsoHash = \MailSo\Base\Utils::Sha1Rand($sEmail.$sPassword);
return \RainLoop\Api::Actions()->Cacher()->Set(\RainLoop\KeyPathHelper::SsoCacherKey($sSsoHash), \RainLoop\Utils::EncodeKeyValues(array(
'Email' => $sEmail,
'Password' => $sPassword,
'AdditionalOptions' => $aAdditionalOptions,
'Time' => $bUseTimeout ? \time() : 0
))) ? $sSsoHash : '';
return \RainLoop\Api::Actions()->Cacher()->Set(\RainLoop\KeyPathHelper::SsoCacherKey($sSsoHash),
\RainLoop\Utils::EncodeKeyValuesQ(array(
'Email' => $sEmail,
'Password' => $sPassword,
'AdditionalOptions' => $aAdditionalOptions,
'Time' => $bUseTimeout ? \time() : 0
))) ? $sSsoHash : '';
}
/**

View file

@ -70,6 +70,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'allow_languages_on_settings' => array(true, 'Allow language selection on settings screen'),
'allow_additional_accounts' => array(true, ''),
'allow_additional_identities' => array(true, ''),
'messages_per_page' => array(20, ' Number of messages displayed on page by default'),

View file

@ -16,6 +16,7 @@ class Capa
const FILTERS = 'FILTERS';
const ATTACHMENT_THUMBNAILS = 'ATTACHMENT_THUMBNAILS';
const ADDITIONAL_ACCOUNTS = 'ADDITIONAL_ACCOUNTS';
const IDENTITIES = 'IDENTITIES';
const TEMPLATES = 'TEMPLATES';
const AUTOLOGOUT = 'AUTOLOGOUT';
}

View file

@ -366,6 +366,26 @@ class Account extends \RainLoop\Account // for backward compatibility
));
}
/**
* @return string
*/
public function GetAuthTokenQ()
{
return \RainLoop\Utils::EncodeKeyValuesQ(array(
'token', // 0
$this->sEmail, // 1
$this->sLogin, // 2
$this->sPassword, // 3
\RainLoop\Utils::Fingerprint(), // 4
$this->sSignMeToken, // 5
$this->sParentEmail, // 6
\RainLoop\Utils::GetShortToken(), // 7
$this->sProxyAuthUser, // 8
$this->sProxyAuthPassword, // 9
0 // 10 // timelife
));
}
/**
* @param \RainLoop\Plugins\Manager $oPlugins
* @param \MailSo\Mail\MailClient $oMailClient

View file

@ -75,11 +75,13 @@ class Identity
}
/**
* @param bool $bFillOnEmpty = false
*
* @return string
*/
public function Id()
public function Id($bFillOnEmpty = false)
{
return $this->sId;
return $bFillOnEmpty ? ('' === $this->sId ? '---' : $this->sId) : $this->sId;
}
/**
@ -192,4 +194,12 @@ class Identity
{
return !empty($this->sEmail);
}
/**
* @return bool
*/
public function IsAccountIdentities()
{
return '' === $this->Id();
}
}

View file

@ -429,7 +429,7 @@ class ServiceActions
{
$this->oActions->verifyCacheByKey($sData);
$aData = \RainLoop\Utils::DecodeKeyValues($sData);
$aData = \RainLoop\Utils::DecodeKeyValuesQ($sData);
if (\is_array($aData) && !empty($aData['Token']) && !empty($aData['Url']) && $aData['Token'] === \RainLoop\Utils::GetConnectionToken())
{
$iCode = 404;
@ -892,7 +892,7 @@ class ServiceActions
$sSsoSubData = $this->Cacher()->Get(\RainLoop\KeyPathHelper::SsoCacherKey($sSsoHash));
if (!empty($sSsoSubData))
{
$mData = \RainLoop\Utils::DecodeKeyValues($sSsoSubData);
$mData = \RainLoop\Utils::DecodeKeyValuesQ($sSsoSubData);
$this->Cacher()->Delete(\RainLoop\KeyPathHelper::SsoCacherKey($sSsoHash));
if (\is_array($mData) && !empty($mData['Email']) && isset($mData['Password'], $mData['Time']) &&

View file

@ -6,6 +6,10 @@ class Utils
{
static $Cookies = null;
static $RSA = null;
static $RsaKey = null;
/**
* @return void
*/
@ -31,6 +35,145 @@ class Utils
return false;
}
/**
* @return \Crypt_RSA|null
*/
static public function CryptRSA()
{
if (null === \RainLoop\Utils::$RSA)
{
if (!\defined('_phpseclib_'))
{
\set_include_path(\get_include_path().PATH_SEPARATOR.APP_VERSION_ROOT_PATH.'app/libraries/phpseclib');
define('_phpseclib_', true);
}
if (!\class_exists('Crypt_RSA'))
{
include_once 'Crypt/RSA.php';
\defined('CRYPT_RSA_MODE') || \define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
}
if (\class_exists('Crypt_RSA'))
{
$oRsa = new \Crypt_RSA();
$oRsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$oRsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$oRsa->setPrivateKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$sPrivateKey = \file_exists(APP_PRIVATE_DATA.'rsa/private') ?
\file_get_contents(APP_PRIVATE_DATA.'rsa/private') : '';
if (!empty($sPrivateKey))
{
$oRsa->loadKey($sPrivateKey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$oRsa->loadKey($oRsa->getPublicKey(), CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
\RainLoop\Utils::$RSA = $oRsa;
}
}
}
return \RainLoop\Utils::$RSA;
}
/**
* @return string
*/
static public function RsaPrivateKey()
{
if (!empty(\RainLoop\Utils::$RsaKey))
{
return \RainLoop\Utils::$RsaKey;
}
\RainLoop\Utils::$RsaKey = \file_exists(APP_PRIVATE_DATA.'rsa/private') ?
\file_get_contents(APP_PRIVATE_DATA.'rsa/private') : '';
\RainLoop\Utils::$RsaKey = \is_string(\RainLoop\Utils::$RsaKey) ? \RainLoop\Utils::$RsaKey : '';
}
/**
* @param string $sString
* @param string $sKey = ''
*
* @return string|false
*/
static public function EncryptStringRSA($sString, $sKey = '')
{
$sResult = '';
$sKey = \md5($sKey);
$sPrivateKey = \RainLoop\Utils::RsaPrivateKey();
if (!empty($sPrivateKey))
{
$oPrivKey = \openssl_pkey_get_private($sPrivateKey);
$oKeyDetails = \openssl_pkey_get_details($oPrivKey);
if (!empty($oKeyDetails['key']) && !empty($oKeyDetails['bits']))
{
$oPubKey = \openssl_pkey_get_public($oKeyDetails['key']);
$iC = (($oKeyDetails['bits'] / 8) - 15);
$aString = \str_split($sString, $iC);
foreach ($aString as $iIndex => $sLine)
{
$sEncrypted = '';
\openssl_public_encrypt($sLine, $sEncrypted, $oPubKey);
$aString[$iIndex] = $sEncrypted;
}
$aString[] = $sKey;
$sResult = @\serialize($aString);
\openssl_free_key($oPubKey);
}
\openssl_free_key($oPrivKey);
}
return $sResult;
}
/**
* @param string $sString
* @param string $sKey = ''
*
* @return string|false
*/
static public function DecryptStringRSA($sString, $sKey = '')
{
$sResult = '';
$sKey = \md5($sKey);
$sPrivateKey = \RainLoop\Utils::RsaPrivateKey();
if (!empty($sPrivateKey) && !empty($sString))
{
$oPrivKey = \openssl_pkey_get_private($sPrivateKey);
$aString = @\unserialize($sString);
if (\is_array($aString))
{
if ($sKey === \array_pop($aString))
{
foreach ($aString as $iIndex => $sLine)
{
$sDecrypted = '';
\openssl_private_decrypt($sLine, $sDecrypted, $oPrivKey);
$aString[$iIndex] = $sDecrypted;
}
$sResult = \implode('', $aString);
}
}
\openssl_free_key($oPrivKey);
}
return $sResult;
}
/**
* @param string $sString
@ -54,6 +197,42 @@ class Utils
return \MailSo\Base\Crypt::XxteaDecrypt($sEncriptedString, $sKey);
}
/**
* @param string $sString
* @param string $sKey
*
* @return string
*/
static public function EncryptStringQ($sString, $sKey)
{
// if (\MailSo\Base\Utils::FunctionExistsAndEnabled('openssl_pkey_get_private'))
// {
// return \RainLoop\Utils::EncryptStringRSA($sString,
// $sKey.'Q'.\RainLoop\Utils::GetShortToken());
// }
return \MailSo\Base\Crypt::XxteaEncrypt($sString,
$sKey.'Q'.\RainLoop\Utils::GetShortToken());
}
/**
* @param string $sEncriptedString
* @param string $sKey
*
* @return string
*/
static public function DecryptStringQ($sEncriptedString, $sKey)
{
// if (\MailSo\Base\Utils::FunctionExistsAndEnabled('openssl_pkey_get_private'))
// {
// return \RainLoop\Utils::DecryptStringRSA($sEncriptedString,
// $sKey.'Q'.\RainLoop\Utils::GetShortToken());
// }
return \MailSo\Base\Crypt::XxteaDecrypt($sEncriptedString,
$sKey.'Q'.\RainLoop\Utils::GetShortToken());
}
/**
* @param array $aValues
* @param string $sCustomKey = ''
@ -63,7 +242,7 @@ class Utils
static public function EncodeKeyValues(array $aValues, $sCustomKey = '')
{
return \MailSo\Base\Utils::UrlSafeBase64Encode(
\RainLoop\Utils::EncryptString(\serialize($aValues), \md5(APP_SALT.$sCustomKey)));
\RainLoop\Utils::EncryptString(@\serialize($aValues), \md5(APP_SALT.$sCustomKey)));
}
/**
@ -74,13 +253,41 @@ class Utils
*/
static public function DecodeKeyValues($sEncodedValues, $sCustomKey = '')
{
$aResult = \unserialize(
$aResult = @\unserialize(
\RainLoop\Utils::DecryptString(
\MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), \md5(APP_SALT.$sCustomKey)));
return \is_array($aResult) ? $aResult : array();
}
/**
* @param array $aValues
* @param string $sCustomKey = ''
*
* @return string
*/
static public function EncodeKeyValuesQ(array $aValues, $sCustomKey = '')
{
return \MailSo\Base\Utils::UrlSafeBase64Encode(
\RainLoop\Utils::EncryptStringQ(
@\serialize($aValues), \md5(APP_SALT.$sCustomKey)));
}
/**
* @param string $sEncodedValues
* @param string $sCustomKey = ''
*
* @return array
*/
static public function DecodeKeyValuesQ($sEncodedValues, $sCustomKey = '')
{
$aResult = @\unserialize(
\RainLoop\Utils::DecryptStringQ(
\MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), \md5(APP_SALT.$sCustomKey)));
return \is_array($aResult) ? $aResult : array();
}
/**
* @return string
*/
@ -296,33 +503,33 @@ class Utils
*/
public static function GetCookie($sName, $mDefault = null)
{
if (null === self::$Cookies)
if (null === \RainLoop\Utils::$Cookies)
{
self::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
\RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
}
return isset(self::$Cookies[$sName]) ? self::$Cookies[$sName] : $mDefault;
return isset(\RainLoop\Utils::$Cookies[$sName]) ? \RainLoop\Utils::$Cookies[$sName] : $mDefault;
}
public static function SetCookie($sName, $sValue = '', $iExpire = 0, $sPath = '/', $sDomain = '', $sSecure = false, $bHttpOnly = false)
{
if (null === self::$Cookies)
if (null === \RainLoop\Utils::$Cookies)
{
self::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
\RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
}
self::$Cookies[$sName] = $sValue;
\RainLoop\Utils::$Cookies[$sName] = $sValue;
@\setcookie($sName, $sValue, $iExpire, $sPath, $sDomain, $sSecure, $bHttpOnly);
}
public static function ClearCookie($sName)
{
if (null === self::$Cookies)
if (null === \RainLoop\Utils::$Cookies)
{
self::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
\RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array();
}
unset(self::$Cookies[$sName]);
unset(\RainLoop\Utils::$Cookies[$sName]);
@\setcookie($sName, '', \time() - 3600 * 24 * 30, '/');
}