mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-08 17:07:03 +03:00
Move and improve cookie handling so that it might catch #537
This commit is contained in:
parent
fa3d4727f9
commit
977dc1e5e4
10 changed files with 180 additions and 183 deletions
|
|
@ -748,9 +748,9 @@ class Actions
|
||||||
$aResult['ContactsSync'] = $aData;
|
$aResult['ContactsSync'] = $aData;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sToken = Utils::GetCookie(self::AUTH_MAILTO_TOKEN_KEY);
|
$sToken = \SnappyMail\Cookies::get(self::AUTH_MAILTO_TOKEN_KEY);
|
||||||
if (null !== $sToken) {
|
if (null !== $sToken) {
|
||||||
Utils::ClearCookie(self::AUTH_MAILTO_TOKEN_KEY);
|
\SnappyMail\Cookies::clear(self::AUTH_MAILTO_TOKEN_KEY);
|
||||||
|
|
||||||
$mMailToData = Utils::DecodeKeyValuesQ($sToken);
|
$mMailToData = Utils::DecodeKeyValuesQ($sToken);
|
||||||
if (!empty($mMailToData['MailTo']) && 'MailTo' === $mMailToData['MailTo'] && !empty($mMailToData['To'])) {
|
if (!empty($mMailToData['MailTo']) && 'MailTo' === $mMailToData['MailTo'] && !empty($mMailToData['To'])) {
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ trait Accounts
|
||||||
$bReload = false;
|
$bReload = false;
|
||||||
$oAccount = $this->getAccountFromToken();
|
$oAccount = $this->getAccountFromToken();
|
||||||
if ($oAccount instanceof AdditionalAccount && $oAccount->Email() === $sEmailToDelete) {
|
if ($oAccount instanceof AdditionalAccount && $oAccount->Email() === $sEmailToDelete) {
|
||||||
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
\SnappyMail\Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||||
$bReload = true;
|
$bReload = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ trait Admin
|
||||||
|
|
||||||
protected function getAdminAuthKey() : string
|
protected function getAdminAuthKey() : string
|
||||||
{
|
{
|
||||||
$cookie = Utils::GetCookie(static::$AUTH_ADMIN_TOKEN_KEY);
|
$cookie = \SnappyMail\Cookies::get(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||||
if ($cookie) {
|
if ($cookie) {
|
||||||
$aAdminHash = Utils::DecodeKeyValuesQ($cookie);
|
$aAdminHash = Utils::DecodeKeyValuesQ($cookie);
|
||||||
if (!empty($aAdminHash[1]) && 'token' === $aAdminHash[0]) {
|
if (!empty($aAdminHash[1]) && 'token' === $aAdminHash[0]) {
|
||||||
return $aAdminHash[1];
|
return $aAdminHash[1];
|
||||||
}
|
}
|
||||||
Utils::ClearCookie(static::$AUTH_ADMIN_TOKEN_KEY);
|
\SnappyMail\Cookies::clear(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use RainLoop\Model\MainAccount;
|
||||||
use RainLoop\Model\AdditionalAccount;
|
use RainLoop\Model\AdditionalAccount;
|
||||||
use RainLoop\Providers\Storage\Enumerations\StorageType;
|
use RainLoop\Providers\Storage\Enumerations\StorageType;
|
||||||
use RainLoop\Exceptions\ClientException;
|
use RainLoop\Exceptions\ClientException;
|
||||||
|
use SnappyMail\Cookies;
|
||||||
|
|
||||||
trait UserAuth
|
trait UserAuth
|
||||||
{
|
{
|
||||||
|
|
@ -156,12 +157,12 @@ trait UserAuth
|
||||||
private static function SetAccountCookie(string $sName, ?Account $oAccount)
|
private static function SetAccountCookie(string $sName, ?Account $oAccount)
|
||||||
{
|
{
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
Utils::SetCookie(
|
Cookies::set(
|
||||||
$sName,
|
$sName,
|
||||||
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($oAccount))
|
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($oAccount))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Utils::ClearCookie($sName);
|
Cookies::clear($sName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,7 +210,7 @@ trait UserAuth
|
||||||
$this->getMainAccountFromToken($bThrowExceptionOnFalse);
|
$this->getMainAccountFromToken($bThrowExceptionOnFalse);
|
||||||
|
|
||||||
if (false === $this->oAdditionalAuthAccount && isset($_COOKIE[self::AUTH_ADDITIONAL_TOKEN_KEY])) {
|
if (false === $this->oAdditionalAuthAccount && isset($_COOKIE[self::AUTH_ADDITIONAL_TOKEN_KEY])) {
|
||||||
$aData = Utils::GetSecureCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
$aData = Cookies::getSecure(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||||
if ($aData) {
|
if ($aData) {
|
||||||
$this->oAdditionalAuthAccount = AdditionalAccount::NewInstanceFromTokenArray(
|
$this->oAdditionalAuthAccount = AdditionalAccount::NewInstanceFromTokenArray(
|
||||||
$this,
|
$this,
|
||||||
|
|
@ -219,7 +220,7 @@ trait UserAuth
|
||||||
}
|
}
|
||||||
if (!$this->oAdditionalAuthAccount) {
|
if (!$this->oAdditionalAuthAccount) {
|
||||||
$this->oAdditionalAuthAccount = null;
|
$this->oAdditionalAuthAccount = null;
|
||||||
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,14 +235,14 @@ trait UserAuth
|
||||||
if (false === $this->oMainAuthAccount) try {
|
if (false === $this->oMainAuthAccount) try {
|
||||||
$this->oMainAuthAccount = null;
|
$this->oMainAuthAccount = null;
|
||||||
if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) {
|
if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) {
|
||||||
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
Cookies::clear(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
||||||
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
Cookies::clear(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
// Cookies::clear(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
// Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
// Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||||
Utils::ClearCookie(Utils::SESSION_TOKEN);
|
Cookies::clear(Utils::SESSION_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aData = Utils::GetSecureCookie(self::AUTH_SPEC_TOKEN_KEY);
|
$aData = Cookies::getSecure(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
if ($aData) {
|
if ($aData) {
|
||||||
/**
|
/**
|
||||||
* Server side control/kickout of logged in sessions
|
* Server side control/kickout of logged in sessions
|
||||||
|
|
@ -274,7 +275,7 @@ trait UserAuth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$this->oMainAuthAccount) {
|
if (!$this->oMainAuthAccount) {
|
||||||
Utils::ClearCookie(Utils::SESSION_TOKEN);
|
Cookies::clear(Utils::SESSION_TOKEN);
|
||||||
// \MailSo\Base\Http::StatusHeader(401);
|
// \MailSo\Base\Http::StatusHeader(401);
|
||||||
$this->Logout(true);
|
$this->Logout(true);
|
||||||
// $sAdditionalMessage = $this->StaticI18N('SESSION_GONE');
|
// $sAdditionalMessage = $this->StaticI18N('SESSION_GONE');
|
||||||
|
|
@ -310,7 +311,7 @@ trait UserAuth
|
||||||
$this->oAdditionalAuthAccount = false;
|
$this->oAdditionalAuthAccount = false;
|
||||||
$this->oMainAuthAccount = $oAccount;
|
$this->oMainAuthAccount = $oAccount;
|
||||||
if (!isset($_COOKIE['smctoken'])) {
|
if (!isset($_COOKIE['smctoken'])) {
|
||||||
\RainLoop\Utils::SetCookie('smctoken', \base64_encode(\random_bytes(16)), 0, false);
|
Cookies::set('smctoken', \base64_encode(\random_bytes(16)), 0, false);
|
||||||
}
|
}
|
||||||
static::SetAccountCookie(self::AUTH_SPEC_TOKEN_KEY, $oAccount);
|
static::SetAccountCookie(self::AUTH_SPEC_TOKEN_KEY, $oAccount);
|
||||||
}
|
}
|
||||||
|
|
@ -327,7 +328,7 @@ trait UserAuth
|
||||||
|
|
||||||
private static function GetSignMeToken(): ?array
|
private static function GetSignMeToken(): ?array
|
||||||
{
|
{
|
||||||
$sSignMeToken = Utils::GetCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
$sSignMeToken = Cookies::get(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
if ($sSignMeToken) {
|
if ($sSignMeToken) {
|
||||||
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'decrypt');
|
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'decrypt');
|
||||||
$aResult = \SnappyMail\Crypt::DecryptUrlSafe($sSignMeToken);
|
$aResult = \SnappyMail\Crypt::DecryptUrlSafe($sSignMeToken);
|
||||||
|
|
@ -336,7 +337,7 @@ trait UserAuth
|
||||||
}
|
}
|
||||||
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'invalid');
|
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'invalid');
|
||||||
// Don't clear due to smctoken cookie missing at initialization and login checkbox
|
// Don't clear due to smctoken cookie missing at initialization and login checkbox
|
||||||
// Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
// Cookies::clear(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +347,7 @@ trait UserAuth
|
||||||
$this->ClearSignMeData();
|
$this->ClearSignMeData();
|
||||||
$uuid = \SnappyMail\UUID::generate();
|
$uuid = \SnappyMail\UUID::generate();
|
||||||
$data = \SnappyMail\Crypt::Encrypt($oAccount);
|
$data = \SnappyMail\Crypt::Encrypt($oAccount);
|
||||||
Utils::SetCookie(
|
Cookies::set(
|
||||||
self::AUTH_SIGN_ME_TOKEN_KEY,
|
self::AUTH_SIGN_ME_TOKEN_KEY,
|
||||||
\SnappyMail\Crypt::EncryptUrlSafe([
|
\SnappyMail\Crypt::EncryptUrlSafe([
|
||||||
'e' => $oAccount->Email(),
|
'e' => $oAccount->Email(),
|
||||||
|
|
@ -405,7 +406,7 @@ trait UserAuth
|
||||||
if ($aTokenData) {
|
if ($aTokenData) {
|
||||||
$this->StorageProvider()->Clear($aTokenData['e'], StorageType::SIGN_ME, $aTokenData['u']);
|
$this->StorageProvider()->Clear($aTokenData['e'], StorageType::SIGN_ME, $aTokenData['u']);
|
||||||
}
|
}
|
||||||
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
Cookies::clear(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -415,14 +416,14 @@ trait UserAuth
|
||||||
public function SetAuthLogoutToken(): void
|
public function SetAuthLogoutToken(): void
|
||||||
{
|
{
|
||||||
\header('X-RainLoop-Action: Logout');
|
\header('X-RainLoop-Action: Logout');
|
||||||
Utils::SetCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY, \md5($_SERVER['REQUEST_TIME_FLOAT']));
|
Cookies::set(self::AUTH_SPEC_LOGOUT_TOKEN_KEY, \md5($_SERVER['REQUEST_TIME_FLOAT']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSpecLogoutCustomMgsWithDeletion(): string
|
public function GetSpecLogoutCustomMgsWithDeletion(): string
|
||||||
{
|
{
|
||||||
$sResult = Utils::GetCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY) ?: '';
|
$sResult = Cookies::get(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY) ?: '';
|
||||||
if (\strlen($sResult)) {
|
if (\strlen($sResult)) {
|
||||||
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY);
|
Cookies::clear(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sResult;
|
return $sResult;
|
||||||
|
|
@ -430,14 +431,14 @@ trait UserAuth
|
||||||
|
|
||||||
public function SetSpecLogoutCustomMgsWithDeletion(string $sMessage): void
|
public function SetSpecLogoutCustomMgsWithDeletion(string $sMessage): void
|
||||||
{
|
{
|
||||||
Utils::SetCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY, $sMessage);
|
Cookies::set(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY, $sMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Logout(bool $bMain) : void
|
public function Logout(bool $bMain) : void
|
||||||
{
|
{
|
||||||
// Utils::ClearCookie(Utils::SESSION_TOKEN);
|
// Cookies::clear(Utils::SESSION_TOKEN);
|
||||||
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||||
$bMain && Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
$bMain && Cookies::clear(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
// TODO: kill SignMe data to prevent automatic login?
|
// TODO: kill SignMe data to prevent automatic login?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class ActionsAdmin extends Actions
|
||||||
if ($sAdminKey) {
|
if ($sAdminKey) {
|
||||||
$this->Cacher(null, true)->Delete(KeyPathHelper::SessionAdminKey($sAdminKey));
|
$this->Cacher(null, true)->Delete(KeyPathHelper::SessionAdminKey($sAdminKey));
|
||||||
}
|
}
|
||||||
Utils::ClearCookie(static::$AUTH_ADMIN_TOKEN_KEY);
|
\SnappyMail\Cookies::clear(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||||
return $this->TrueResponse();
|
return $this->TrueResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,7 +331,7 @@ class ActionsAdmin extends Actions
|
||||||
if (!$sToken) {
|
if (!$sToken) {
|
||||||
throw new \RuntimeException('Failed to encode admin token');
|
throw new \RuntimeException('Failed to encode admin token');
|
||||||
}
|
}
|
||||||
Utils::SetCookie(static::$AUTH_ADMIN_TOKEN_KEY, $sToken);
|
\SnappyMail\Cookies::set(static::$AUTH_ADMIN_TOKEN_KEY, $sToken);
|
||||||
return $sToken;
|
return $sToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,6 @@ namespace RainLoop;
|
||||||
abstract class Api
|
abstract class Api
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function Handle() : bool
|
|
||||||
{
|
|
||||||
static $bOne = false;
|
|
||||||
if (!$bOne) {
|
|
||||||
static::SetupDefaultConfig();
|
|
||||||
$bOne = true;
|
|
||||||
}
|
|
||||||
return $bOne;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function Actions() : Actions
|
public static function Actions() : Actions
|
||||||
{
|
{
|
||||||
static $oActions = null;
|
static $oActions = null;
|
||||||
|
|
@ -40,6 +30,7 @@ abstract class Api
|
||||||
// \ini_set('display_errors', '1');
|
// \ini_set('display_errors', '1');
|
||||||
\ini_set('log_errors', '1');
|
\ini_set('log_errors', '1');
|
||||||
}
|
}
|
||||||
|
\MailSo\Config::$BoundaryPrefix = \trim($oConfig->Get('labs', 'boundary_prefix', ''));
|
||||||
}
|
}
|
||||||
return $oConfig;
|
return $oConfig;
|
||||||
}
|
}
|
||||||
|
|
@ -82,17 +73,6 @@ abstract class Api
|
||||||
return $oLogger;
|
return $oLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function SetupDefaultConfig() : void
|
|
||||||
{
|
|
||||||
\MailSo\Config::$BoundaryPrefix = \trim(static::Config()->Get('labs', 'boundary_prefix', ''));
|
|
||||||
|
|
||||||
Utils::$CookieDefaultPath = static::Config()->Get('labs', 'cookie_default_path', '');
|
|
||||||
Utils::$CookieSameSite = static::Config()->Get('security', 'cookie_samesite', 'Strict');
|
|
||||||
Utils::$CookieSecure = isset($_SERVER['HTTPS'])
|
|
||||||
|| 'None' == Utils::$CookieSameSite
|
|
||||||
|| !!static::Config()->Get('labs', 'cookie_default_secure', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function Version() : string
|
public static function Version() : string
|
||||||
{
|
{
|
||||||
return APP_VERSION;
|
return APP_VERSION;
|
||||||
|
|
|
||||||
|
|
@ -545,7 +545,7 @@ class ServiceActions
|
||||||
$this->oHttp->ServerNoCache();
|
$this->oHttp->ServerNoCache();
|
||||||
$sTo = \trim($_GET['to'] ?? '');
|
$sTo = \trim($_GET['to'] ?? '');
|
||||||
if (!empty($sTo) && \preg_match('/^mailto:/i', $sTo)) {
|
if (!empty($sTo) && \preg_match('/^mailto:/i', $sTo)) {
|
||||||
Utils::SetCookie(
|
\SnappyMail\Cookies::set(
|
||||||
Actions::AUTH_MAILTO_TOKEN_KEY,
|
Actions::AUTH_MAILTO_TOKEN_KEY,
|
||||||
Utils::EncodeKeyValuesQ(array(
|
Utils::EncodeKeyValuesQ(array(
|
||||||
'Time' => \microtime(true),
|
'Time' => \microtime(true),
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,6 @@ namespace RainLoop;
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
{
|
{
|
||||||
static $CookieDefaultPath = '';
|
|
||||||
|
|
||||||
static $CookieSecure = null;
|
|
||||||
|
|
||||||
static $CookieSameSite = 'Strict';
|
|
||||||
|
|
||||||
const
|
const
|
||||||
/**
|
/**
|
||||||
* 30 days cookie
|
* 30 days cookie
|
||||||
|
|
@ -60,14 +54,14 @@ class Utils
|
||||||
|
|
||||||
public static function GetSessionToken(bool $generate = true) : ?string
|
public static function GetSessionToken(bool $generate = true) : ?string
|
||||||
{
|
{
|
||||||
$sToken = static::GetCookie(self::SESSION_TOKEN);
|
$sToken = \SnappyMail\Cookies::get(self::SESSION_TOKEN);
|
||||||
if (!$sToken) {
|
if (!$sToken) {
|
||||||
if (!$generate) {
|
if (!$generate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
\SnappyMail\Log::debug('TOKENS', 'New SESSION_TOKEN');
|
\SnappyMail\Log::debug('TOKENS', 'New SESSION_TOKEN');
|
||||||
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
||||||
static::SetCookie(self::SESSION_TOKEN, $sToken);
|
\SnappyMail\Cookies::set(self::SESSION_TOKEN, $sToken);
|
||||||
}
|
}
|
||||||
return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT);
|
return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT);
|
||||||
}
|
}
|
||||||
|
|
@ -79,10 +73,10 @@ class Utils
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
return $oAccount->Hash();
|
return $oAccount->Hash();
|
||||||
}
|
}
|
||||||
$sToken = static::GetCookie(self::CONNECTION_TOKEN);
|
$sToken = \SnappyMail\Cookies::get(self::CONNECTION_TOKEN);
|
||||||
if (!$sToken) {
|
if (!$sToken) {
|
||||||
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
||||||
static::SetCookie(self::CONNECTION_TOKEN, $sToken, \time() + 3600 * 24 * 30);
|
\SnappyMail\Cookies::set(self::CONNECTION_TOKEN, $sToken, \time() + 3600 * 24 * 30);
|
||||||
}
|
}
|
||||||
return \sha1('Connection'.APP_SALT.$sToken.'Token'.APP_SALT);
|
return \sha1('Connection'.APP_SALT.$sToken.'Token'.APP_SALT);
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +88,9 @@ class Utils
|
||||||
|
|
||||||
public static function UpdateConnectionToken() : void
|
public static function UpdateConnectionToken() : void
|
||||||
{
|
{
|
||||||
$sToken = static::GetCookie(self::CONNECTION_TOKEN);
|
$sToken = \SnappyMail\Cookies::get(self::CONNECTION_TOKEN);
|
||||||
if ($sToken) {
|
if ($sToken) {
|
||||||
static::SetCookie(self::CONNECTION_TOKEN, $sToken, \time() + 3600 * 24 * 30);
|
\SnappyMail\Cookies::set(self::CONNECTION_TOKEN, $sToken, \time() + 3600 * 24 * 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,118 +104,6 @@ class Utils
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $mDefault = null
|
|
||||||
*/
|
|
||||||
public static function GetCookie(string $sName) : ?string
|
|
||||||
{
|
|
||||||
if (isset($_COOKIE[$sName])) {
|
|
||||||
$aParts = [];
|
|
||||||
foreach (\array_keys($_COOKIE) as $sCookieName) {
|
|
||||||
if (\strtok($sCookieName, '~') === $sName) {
|
|
||||||
$aParts[$sCookieName] = $_COOKIE[$sCookieName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
\ksort($aParts);
|
|
||||||
return \implode('', $aParts);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function GetSecureCookie(string $sName)
|
|
||||||
{
|
|
||||||
return isset($_COOKIE[$sName])
|
|
||||||
? \SnappyMail\Crypt::DecryptFromJSON(\MailSo\Base\Utils::UrlSafeBase64Decode(static::GetCookie($sName)))
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function _SetCookie(string $sName, string $sValue, int $iExpire, bool $httponly = true)
|
|
||||||
{
|
|
||||||
$sPath = static::$CookieDefaultPath;
|
|
||||||
$sPath = $sPath && \strlen($sPath) ? $sPath : '/';
|
|
||||||
/*
|
|
||||||
if (\strlen($sValue) > 4000 - \strlen($sPath . $sName)) {
|
|
||||||
throw new \Exception("Cookie '{$sName}' value too long");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (\strlen($sValue)) {
|
|
||||||
$_COOKIE[$sName] = $sValue;
|
|
||||||
} else {
|
|
||||||
if (!isset($_COOKIE[$sName])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unset($_COOKIE[$sName]);
|
|
||||||
$iExpire = \time() - 3600 * 24 * 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cookie "$sName" has been rejected because it is already expired.
|
|
||||||
// Happens when \setcookie() sends multiple with the same name (and one is deleted)
|
|
||||||
// So when previously set, we must delete all 'Set-Cookie' headers and start over
|
|
||||||
$cookies = [];
|
|
||||||
$cookie_remove = false;
|
|
||||||
foreach (\headers_list() as $header) {
|
|
||||||
if (\preg_match("/Set-Cookie:([^=]+)=/i", $header, $match)) {
|
|
||||||
if (\trim($match[1]) == $sName) {
|
|
||||||
$cookie_remove = true;
|
|
||||||
} else {
|
|
||||||
$cookies[] = $header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($cookie_remove) {
|
|
||||||
\header_remove('Set-Cookie');
|
|
||||||
foreach ($cookies as $cookie) {
|
|
||||||
\header($cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\setcookie($sName, $sValue, array(
|
|
||||||
'expires' => $iExpire,
|
|
||||||
'path' => $sPath,
|
|
||||||
// 'domain' => null,
|
|
||||||
'secure' => static::$CookieSecure,
|
|
||||||
'httponly' => $httponly,
|
|
||||||
'samesite' => static::$CookieSameSite
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Firefox: Cookie "$sName" has been rejected because it is already expired.
|
|
||||||
* \header_remove("set-cookie: {$sName}");
|
|
||||||
*/
|
|
||||||
public static function SetCookie(string $sName, string $sValue, int $iExpire = 0, bool $httponly = true)
|
|
||||||
{
|
|
||||||
$sPath = static::$CookieDefaultPath;
|
|
||||||
$sPath = $sPath && \strlen($sPath) ? $sPath : '/';
|
|
||||||
// https://github.com/the-djmaze/snappymail/issues/451
|
|
||||||
// The 4K browser limit is for the entire cookie, including name, value, expiry date etc.
|
|
||||||
$iMaxSize = 4000 - \strlen($sPath . $sName);
|
|
||||||
/*
|
|
||||||
if ($iMaxSize < \strlen($sValue)) {
|
|
||||||
throw new \Exception("Cookie '{$sName}' value too long");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// Set the new 4K split cookie
|
|
||||||
foreach (\str_split($sValue, $iMaxSize) as $i => $sPart) {
|
|
||||||
$sCookieName = $i ? "{$sName}~{$i}" : $sName;
|
|
||||||
\SnappyMail\Log::debug('COOKIE', "set {$sCookieName}");
|
|
||||||
static::_SetCookie($sCookieName, $sPart, $iExpire);
|
|
||||||
}
|
|
||||||
// Delete unused old 4K split cookie parts
|
|
||||||
foreach (\array_keys($_COOKIE) as $sCookieName) {
|
|
||||||
$aSplit = \explode('~', $sCookieName);
|
|
||||||
if (isset($aSplit[1]) && $aSplit[0] == $sName && $aSplit[1] > $i) {
|
|
||||||
\SnappyMail\Log::debug('COOKIE', "unset {$sCookieName}");
|
|
||||||
static::_SetCookie($sCookieName, '', 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ClearCookie(string $sName)
|
|
||||||
{
|
|
||||||
static::_SetCookie($sName, '', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function UrlEncode(string $sV, bool $bEncode = false) : string
|
public static function UrlEncode(string $sV, bool $bEncode = false) : string
|
||||||
{
|
{
|
||||||
return $bEncode ? \urlencode($sV) : $sV;
|
return $bEncode ? \urlencode($sV) : $sV;
|
||||||
|
|
|
||||||
138
snappymail/v/0.0.0/app/libraries/snappymail/cookies.php
Normal file
138
snappymail/v/0.0.0/app/libraries/snappymail/cookies.php
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail;
|
||||||
|
|
||||||
|
class Cookies
|
||||||
|
{
|
||||||
|
static $DefaultPath = '';
|
||||||
|
|
||||||
|
static $Secure = null;
|
||||||
|
|
||||||
|
static $SameSite = 'Strict';
|
||||||
|
|
||||||
|
private static function init() : bool
|
||||||
|
{
|
||||||
|
static $bOne = false;
|
||||||
|
if (!$bOne) {
|
||||||
|
$oConfig = \RainLoop\Api::Config();
|
||||||
|
static::$DefaultPath = $oConfig->Get('labs', 'cookie_default_path', '');
|
||||||
|
static::$SameSite = $oConfig->Get('security', 'cookie_samesite', 'Strict');
|
||||||
|
static::$Secure = isset($_SERVER['HTTPS'])
|
||||||
|
|| 'None' == static::$SameSite
|
||||||
|
|| !!$oConfig->Get('labs', 'cookie_default_secure', false);
|
||||||
|
$bOne = true;
|
||||||
|
}
|
||||||
|
return $bOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get(string $sName) : ?string
|
||||||
|
{
|
||||||
|
if (isset($_COOKIE[$sName])) {
|
||||||
|
$aParts = [];
|
||||||
|
foreach (\array_keys($_COOKIE) as $sCookieName) {
|
||||||
|
if (\strtok($sCookieName, '~') === $sName) {
|
||||||
|
$aParts[$sCookieName] = $_COOKIE[$sCookieName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\ksort($aParts);
|
||||||
|
return \implode('', $aParts);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSecure(string $sName)
|
||||||
|
{
|
||||||
|
return isset($_COOKIE[$sName])
|
||||||
|
? Crypt::DecryptFromJSON(\MailSo\Base\Utils::UrlSafeBase64Decode(static::get($sName)))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function _set(string $sName, string $sValue, int $iExpire, bool $httponly = true) : bool
|
||||||
|
{
|
||||||
|
$sPath = static::$DefaultPath;
|
||||||
|
$sPath = $sPath && \strlen($sPath) ? $sPath : '/';
|
||||||
|
/*
|
||||||
|
if (\strlen($sValue) > 4000 - \strlen($sPath . $sName)) {
|
||||||
|
throw new \Exception("Cookie '{$sName}' value too long");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (\strlen($sValue)) {
|
||||||
|
$_COOKIE[$sName] = $sValue;
|
||||||
|
} else {
|
||||||
|
if (!isset($_COOKIE[$sName])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
unset($_COOKIE[$sName]);
|
||||||
|
$iExpire = \time() - 3600 * 24 * 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cookie "$sName" has been rejected because it is already expired.
|
||||||
|
// Happens when \setcookie() sends multiple with the same name (and one is deleted)
|
||||||
|
// So when previously set, we must delete all 'Set-Cookie' headers and start over
|
||||||
|
$cookies = [];
|
||||||
|
$cookie_remove = false;
|
||||||
|
foreach (\headers_list() as $header) {
|
||||||
|
if (\preg_match("/Set-Cookie:([^=]+)=/i", $header, $match)) {
|
||||||
|
if (\trim($match[1]) == $sName) {
|
||||||
|
$cookie_remove = true;
|
||||||
|
} else {
|
||||||
|
$cookies[] = $header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($cookie_remove) {
|
||||||
|
\header_remove('Set-Cookie');
|
||||||
|
foreach ($cookies as $cookie) {
|
||||||
|
\header($cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return \setcookie($sName, $sValue, array(
|
||||||
|
'expires' => $iExpire,
|
||||||
|
'path' => $sPath,
|
||||||
|
// 'domain' => null,
|
||||||
|
'secure' => static::$Secure,
|
||||||
|
'httponly' => $httponly,
|
||||||
|
'samesite' => static::$SameSite
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firefox: Cookie "$sName" has been rejected because it is already expired.
|
||||||
|
* \header_remove("set-cookie: {$sName}");
|
||||||
|
*/
|
||||||
|
public static function set(string $sName, string $sValue, int $iExpire = 0, bool $httponly = true) : void
|
||||||
|
{
|
||||||
|
static::init();
|
||||||
|
$sPath = static::$DefaultPath;
|
||||||
|
$sPath = $sPath && \strlen($sPath) ? $sPath : '/';
|
||||||
|
// https://github.com/the-djmaze/snappymail/issues/451
|
||||||
|
// The 4K browser limit is for the entire cookie, including name, value, expiry date etc.
|
||||||
|
$iMaxSize = 4000 - \strlen($sPath . $sName);
|
||||||
|
/*
|
||||||
|
if ($iMaxSize < \strlen($sValue)) {
|
||||||
|
throw new \Exception("Cookie '{$sName}' value too long");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Set the new 4K split cookie
|
||||||
|
foreach (\str_split($sValue, $iMaxSize) as $i => $sPart) {
|
||||||
|
$sCookieName = $i ? "{$sName}~{$i}" : $sName;
|
||||||
|
Log::debug('COOKIE', "set {$sCookieName}");
|
||||||
|
static::_set($sCookieName, $sPart, $iExpire);
|
||||||
|
}
|
||||||
|
// Delete unused old 4K split cookie parts
|
||||||
|
foreach (\array_keys($_COOKIE) as $sCookieName) {
|
||||||
|
$aSplit = \explode('~', $sCookieName);
|
||||||
|
if (isset($aSplit[1]) && $aSplit[0] == $sName && $aSplit[1] > $i) {
|
||||||
|
Log::debug('COOKIE', "unset {$sCookieName}");
|
||||||
|
static::_set($sCookieName, '', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function clear(string $sName) : void
|
||||||
|
{
|
||||||
|
static::init();
|
||||||
|
static::_set($sName, '', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -165,11 +165,7 @@ if (!empty($_ENV['CPANEL']) && !is_dir(APP_PLUGINS_PATH.'login-remote')) {
|
||||||
require __DIR__ . '/cpanel.php';
|
require __DIR__ . '/cpanel.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists('RainLoop\\Api')) {
|
if (class_exists('RainLoop\\Api') && empty($_ENV['SNAPPYMAIL_INCLUDE_AS_API'])) {
|
||||||
RainLoop\Api::Handle();
|
|
||||||
// NextCloud/OwnCloud?
|
|
||||||
if (empty($_ENV['SNAPPYMAIL_INCLUDE_AS_API'])) {
|
|
||||||
RainLoop\Service::Handle();
|
RainLoop\Service::Handle();
|
||||||
exit(0);
|
exit;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue