mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-30 12:39:20 +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;
|
||||
}
|
||||
|
||||
$sToken = Utils::GetCookie(self::AUTH_MAILTO_TOKEN_KEY);
|
||||
$sToken = \SnappyMail\Cookies::get(self::AUTH_MAILTO_TOKEN_KEY);
|
||||
if (null !== $sToken) {
|
||||
Utils::ClearCookie(self::AUTH_MAILTO_TOKEN_KEY);
|
||||
\SnappyMail\Cookies::clear(self::AUTH_MAILTO_TOKEN_KEY);
|
||||
|
||||
$mMailToData = Utils::DecodeKeyValuesQ($sToken);
|
||||
if (!empty($mMailToData['MailTo']) && 'MailTo' === $mMailToData['MailTo'] && !empty($mMailToData['To'])) {
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ trait Accounts
|
|||
$bReload = false;
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
if ($oAccount instanceof AdditionalAccount && $oAccount->Email() === $sEmailToDelete) {
|
||||
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
\SnappyMail\Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
$bReload = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ trait Admin
|
|||
|
||||
protected function getAdminAuthKey() : string
|
||||
{
|
||||
$cookie = Utils::GetCookie(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||
$cookie = \SnappyMail\Cookies::get(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||
if ($cookie) {
|
||||
$aAdminHash = Utils::DecodeKeyValuesQ($cookie);
|
||||
if (!empty($aAdminHash[1]) && 'token' === $aAdminHash[0]) {
|
||||
return $aAdminHash[1];
|
||||
}
|
||||
Utils::ClearCookie(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||
\SnappyMail\Cookies::clear(static::$AUTH_ADMIN_TOKEN_KEY);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use RainLoop\Model\MainAccount;
|
|||
use RainLoop\Model\AdditionalAccount;
|
||||
use RainLoop\Providers\Storage\Enumerations\StorageType;
|
||||
use RainLoop\Exceptions\ClientException;
|
||||
use SnappyMail\Cookies;
|
||||
|
||||
trait UserAuth
|
||||
{
|
||||
|
|
@ -156,12 +157,12 @@ trait UserAuth
|
|||
private static function SetAccountCookie(string $sName, ?Account $oAccount)
|
||||
{
|
||||
if ($oAccount) {
|
||||
Utils::SetCookie(
|
||||
Cookies::set(
|
||||
$sName,
|
||||
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($oAccount))
|
||||
);
|
||||
} else {
|
||||
Utils::ClearCookie($sName);
|
||||
Cookies::clear($sName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +210,7 @@ trait UserAuth
|
|||
$this->getMainAccountFromToken($bThrowExceptionOnFalse);
|
||||
|
||||
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) {
|
||||
$this->oAdditionalAuthAccount = AdditionalAccount::NewInstanceFromTokenArray(
|
||||
$this,
|
||||
|
|
@ -219,7 +220,7 @@ trait UserAuth
|
|||
}
|
||||
if (!$this->oAdditionalAuthAccount) {
|
||||
$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 {
|
||||
$this->oMainAuthAccount = null;
|
||||
if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) {
|
||||
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
||||
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||
// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||
// Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
Utils::ClearCookie(Utils::SESSION_TOKEN);
|
||||
Cookies::clear(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
||||
Cookies::clear(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||
// Cookies::clear(self::AUTH_SPEC_TOKEN_KEY);
|
||||
// Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
Cookies::clear(Utils::SESSION_TOKEN);
|
||||
}
|
||||
|
||||
$aData = Utils::GetSecureCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||
$aData = Cookies::getSecure(self::AUTH_SPEC_TOKEN_KEY);
|
||||
if ($aData) {
|
||||
/**
|
||||
* Server side control/kickout of logged in sessions
|
||||
|
|
@ -274,7 +275,7 @@ trait UserAuth
|
|||
}
|
||||
}
|
||||
if (!$this->oMainAuthAccount) {
|
||||
Utils::ClearCookie(Utils::SESSION_TOKEN);
|
||||
Cookies::clear(Utils::SESSION_TOKEN);
|
||||
// \MailSo\Base\Http::StatusHeader(401);
|
||||
$this->Logout(true);
|
||||
// $sAdditionalMessage = $this->StaticI18N('SESSION_GONE');
|
||||
|
|
@ -310,7 +311,7 @@ trait UserAuth
|
|||
$this->oAdditionalAuthAccount = false;
|
||||
$this->oMainAuthAccount = $oAccount;
|
||||
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);
|
||||
}
|
||||
|
|
@ -327,7 +328,7 @@ trait UserAuth
|
|||
|
||||
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) {
|
||||
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'decrypt');
|
||||
$aResult = \SnappyMail\Crypt::DecryptUrlSafe($sSignMeToken);
|
||||
|
|
@ -336,7 +337,7 @@ trait UserAuth
|
|||
}
|
||||
\SnappyMail\Log::notice(self::AUTH_SIGN_ME_TOKEN_KEY, 'invalid');
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -346,7 +347,7 @@ trait UserAuth
|
|||
$this->ClearSignMeData();
|
||||
$uuid = \SnappyMail\UUID::generate();
|
||||
$data = \SnappyMail\Crypt::Encrypt($oAccount);
|
||||
Utils::SetCookie(
|
||||
Cookies::set(
|
||||
self::AUTH_SIGN_ME_TOKEN_KEY,
|
||||
\SnappyMail\Crypt::EncryptUrlSafe([
|
||||
'e' => $oAccount->Email(),
|
||||
|
|
@ -405,7 +406,7 @@ trait UserAuth
|
|||
if ($aTokenData) {
|
||||
$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
|
||||
{
|
||||
\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
|
||||
{
|
||||
$sResult = Utils::GetCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY) ?: '';
|
||||
$sResult = Cookies::get(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY) ?: '';
|
||||
if (\strlen($sResult)) {
|
||||
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY);
|
||||
Cookies::clear(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY);
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
|
|
@ -430,14 +431,14 @@ trait UserAuth
|
|||
|
||||
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
|
||||
{
|
||||
// Utils::ClearCookie(Utils::SESSION_TOKEN);
|
||||
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
$bMain && Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||
// Cookies::clear(Utils::SESSION_TOKEN);
|
||||
Cookies::clear(self::AUTH_ADDITIONAL_TOKEN_KEY);
|
||||
$bMain && Cookies::clear(self::AUTH_SPEC_TOKEN_KEY);
|
||||
// TODO: kill SignMe data to prevent automatic login?
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class ActionsAdmin extends Actions
|
|||
if ($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();
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ class ActionsAdmin extends Actions
|
|||
if (!$sToken) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,16 +5,6 @@ namespace RainLoop;
|
|||
abstract class Api
|
||||
{
|
||||
|
||||
public static function Handle() : bool
|
||||
{
|
||||
static $bOne = false;
|
||||
if (!$bOne) {
|
||||
static::SetupDefaultConfig();
|
||||
$bOne = true;
|
||||
}
|
||||
return $bOne;
|
||||
}
|
||||
|
||||
public static function Actions() : Actions
|
||||
{
|
||||
static $oActions = null;
|
||||
|
|
@ -40,6 +30,7 @@ abstract class Api
|
|||
// \ini_set('display_errors', '1');
|
||||
\ini_set('log_errors', '1');
|
||||
}
|
||||
\MailSo\Config::$BoundaryPrefix = \trim($oConfig->Get('labs', 'boundary_prefix', ''));
|
||||
}
|
||||
return $oConfig;
|
||||
}
|
||||
|
|
@ -82,17 +73,6 @@ abstract class Api
|
|||
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
|
||||
{
|
||||
return APP_VERSION;
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ class ServiceActions
|
|||
$this->oHttp->ServerNoCache();
|
||||
$sTo = \trim($_GET['to'] ?? '');
|
||||
if (!empty($sTo) && \preg_match('/^mailto:/i', $sTo)) {
|
||||
Utils::SetCookie(
|
||||
\SnappyMail\Cookies::set(
|
||||
Actions::AUTH_MAILTO_TOKEN_KEY,
|
||||
Utils::EncodeKeyValuesQ(array(
|
||||
'Time' => \microtime(true),
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ namespace RainLoop;
|
|||
|
||||
class Utils
|
||||
{
|
||||
static $CookieDefaultPath = '';
|
||||
|
||||
static $CookieSecure = null;
|
||||
|
||||
static $CookieSameSite = 'Strict';
|
||||
|
||||
const
|
||||
/**
|
||||
* 30 days cookie
|
||||
|
|
@ -60,14 +54,14 @@ class Utils
|
|||
|
||||
public static function GetSessionToken(bool $generate = true) : ?string
|
||||
{
|
||||
$sToken = static::GetCookie(self::SESSION_TOKEN);
|
||||
$sToken = \SnappyMail\Cookies::get(self::SESSION_TOKEN);
|
||||
if (!$sToken) {
|
||||
if (!$generate) {
|
||||
return null;
|
||||
}
|
||||
\SnappyMail\Log::debug('TOKENS', 'New SESSION_TOKEN');
|
||||
$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);
|
||||
}
|
||||
|
|
@ -79,10 +73,10 @@ class Utils
|
|||
if ($oAccount) {
|
||||
return $oAccount->Hash();
|
||||
}
|
||||
$sToken = static::GetCookie(self::CONNECTION_TOKEN);
|
||||
$sToken = \SnappyMail\Cookies::get(self::CONNECTION_TOKEN);
|
||||
if (!$sToken) {
|
||||
$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);
|
||||
}
|
||||
|
|
@ -94,9 +88,9 @@ class Utils
|
|||
|
||||
public static function UpdateConnectionToken() : void
|
||||
{
|
||||
$sToken = static::GetCookie(self::CONNECTION_TOKEN);
|
||||
$sToken = \SnappyMail\Cookies::get(self::CONNECTION_TOKEN);
|
||||
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
|
||||
{
|
||||
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';
|
||||
}
|
||||
|
||||
if (class_exists('RainLoop\\Api')) {
|
||||
RainLoop\Api::Handle();
|
||||
// NextCloud/OwnCloud?
|
||||
if (empty($_ENV['SNAPPYMAIL_INCLUDE_AS_API'])) {
|
||||
RainLoop\Service::Handle();
|
||||
exit(0);
|
||||
}
|
||||
if (class_exists('RainLoop\\Api') && empty($_ENV['SNAPPYMAIL_INCLUDE_AS_API'])) {
|
||||
RainLoop\Service::Handle();
|
||||
exit;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue