Improved Domain management code

This commit is contained in:
the-djmaze 2024-03-11 11:04:19 +01:00
parent de150b359c
commit df7b11eadb
3 changed files with 104 additions and 136 deletions

View file

@ -1 +1,5 @@
outlook.com,qq.com,yahoo.com,gmail.com,hotmail.com
outlook.com
qq.com
yahoo.com
gmail.com
hotmail.com

View file

@ -165,14 +165,13 @@ abstract class Utils
|| !\preg_match('/[^\x09\x10\x13\x0A\x0D\x20-\x7E]/', $sValue);
}
public static function StrMailDomainToLower(string $sValue) : string
private static function StrMailDomainToLower(string $sValue) : string
{
$aParts = \explode('@', $sValue);
$iLast = \count($aParts) - 1;
if ($iLast) {
$iLast = \count($aParts) - 1;
$aParts[$iLast] = \mb_strtolower($aParts[$iLast]);
}
return \implode('@', $aParts);
}
@ -704,7 +703,10 @@ abstract class Utils
return !empty($sIp) && $sIp === \filter_var($sIp, FILTER_VALIDATE_IP);
}
public static function IdnToUtf8(string $sStr, bool $bLowerIfAscii = false) : string
/**
* Converts xn--du8h.snappymail.eu to 📧.snappymail.eu
*/
public static function IdnToUtf8(string $sStr, bool $bLowerCase = false) : string
{
if (\strlen($sStr) && \preg_match('/(^|\.|@)xn--/i', $sStr)) {
try
@ -714,20 +716,19 @@ abstract class Utils
catch (\Throwable $oException) {}
}
return $bLowerIfAscii ? static::StrMailDomainToLower($sStr) : $sStr;
return $bLowerCase ? static::StrMailDomainToLower($sStr) : $sStr;
}
public static function IdnToAscii(string $sStr, bool $bLowerIfAscii = false) : string
/**
* Converts 📧.snappymail.eu to xn--du8h.snappymail.eu
*/
public static function IdnToAscii(string $sStr, bool $bLowerCase = false) : string
{
$sStr = $bLowerIfAscii ? static::StrMailDomainToLower($sStr) : $sStr;
$sUser = '';
$sDomain = $sStr;
if (false !== \strpos($sStr, '@')) {
$sUser = static::GetAccountNameFromEmail($sStr);
$sDomain = static::GetDomainFromEmail($sStr);
$aParts = \explode('@', $sStr);
$sDomain = \array_pop($aParts);
if ($bLowerCase) {
$sDomain = \mb_strtolower($sDomain);
}
if (\strlen($sDomain) && \preg_match('/[^\x20-\x7E]/', $sDomain)) {
try
{
@ -735,7 +736,7 @@ abstract class Utils
}
catch (\Throwable $oException) {}
}
return ('' === $sUser ? '' : $sUser.'@').$sDomain;
$aParts[] = $sDomain;
return \implode('@', $aParts);
}
}

View file

@ -2,48 +2,40 @@
namespace RainLoop\Providers\Domain;
use MailSo\Cache\CacheClient;
class DefaultDomain implements DomainInterface
{
/**
* @var string
*/
protected $sDomainPath;
const CACHE_KEY = '/WildCard/DomainCache/';
/**
* @var \MailSo\Cache\CacheClient
*/
protected $oCacher;
protected string $sDomainPath;
public function __construct(string $sDomainPath, ?\MailSo\Cache\CacheClient $oCacher = null)
protected ?CacheClient $oCacher;
public function __construct(string $sDomainPath, ?CacheClient $oCacher = null)
{
$this->sDomainPath = \rtrim(\trim($sDomainPath), '\\/');
$this->oCacher = $oCacher;
}
private static function codeFileName(string $sName, bool $bBack = false) : string
private static function decodeFileName(string $sName) : string
{
if ($bBack && 'default' === $sName) {
return '*';
}
if (!$bBack && '*' === $sName) {
return 'default';
}
return $bBack
? \str_replace('_wildcard_', '*', \MailSo\Base\Utils::IdnToUtf8($sName, true))
: \str_replace('*', '_wildcard_', \MailSo\Base\Utils::IdnToAscii($sName, true));
return ('default' === $sName)
? '*'
: \str_replace('_wildcard_', '*', \MailSo\Base\Utils::IdnToUtf8($sName, true));
}
private static function wildcardDomainsCacheKey() : string
private static function encodeFileName(string $sName) : string
{
return '/WildCard/DomainCache/'.\md5(APP_VERSION.APP_PRIVATE_DATA_NAME).'/';
return ('*' === $sName)
? 'default'
: \str_replace('*', '_wildcard_', \MailSo\Base\Utils::IdnToAscii($sName, true));
}
private function getWildcardDomainsLine() : string
{
if ($this->oCacher) {
$sResult = $this->oCacher->Get(static::wildcardDomainsCacheKey());
$sResult = $this->oCacher->Get(static::CACHE_KEY);
if (\strlen($sResult)) {
return $sResult;
}
@ -56,14 +48,21 @@ class DefaultDomain implements DomainInterface
foreach ($aList as $sFile) {
$sName = \substr(\basename($sFile), 0, -5);
if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) {
$aNames[] = static::codeFileName($sName, true);
$aNames[] = static::decodeFileName($sName);
}
}
$aList = \glob($this->sDomainPath.'/*.ini');
foreach ($aList as $sFile) {
$sName = \substr(\basename($sFile), 0, -4);
if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) {
$aNames[] = static::codeFileName($sName, true);
$aNames[] = static::decodeFileName($sName);
}
}
$aList = \glob($this->sDomainPath.'/*.alias');
foreach ($aList as $sFile) {
$sName = \substr(\basename($sFile), 0, -6);
if ('default' === $sName || false !== \strpos($sName, '_wildcard_')) {
$aNames[] = static::decodeFileName($sName);
}
}
@ -74,7 +73,7 @@ class DefaultDomain implements DomainInterface
}
if ($this->oCacher) {
$this->oCacher->Set(static::wildcardDomainsCacheKey(), $sResult);
$this->oCacher->Set(static::CACHE_KEY, $sResult);
}
return $sResult;
@ -82,29 +81,24 @@ class DefaultDomain implements DomainInterface
public function Load(string $sName, bool $bFindWithWildCard = false, bool $bCheckDisabled = true, bool $bCheckAliases = true) : ?\RainLoop\Model\Domain
{
if ($bCheckDisabled) {
$aDisabled = [];
if (\file_exists($this->sDomainPath.'/disabled')) {
$aDisabled = \explode(',', \file_get_contents($this->sDomainPath.'/disabled'));
if ($aDisabled && \in_array(\MailSo\Base\Utils::IdnToAscii($sName, true), $aDisabled)) {
return null;
}
}
$sName = \MailSo\Base\Utils::IdnToUtf8($sName, true);
if ($bCheckDisabled && \in_array($sName, $this->getDisabled())) {
return null;
}
$sRealFileName = static::codeFileName($sName);
$sRealFileBase = $this->sDomainPath . '/' . static::encodeFileName($sName);
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.json')) {
$aDomain = \json_decode(\file_get_contents($this->sDomainPath.'/'.$sRealFileName.'.json'), true) ?: array();
if (\file_exists($sRealFileBase.'.json')) {
$aDomain = \json_decode(\file_get_contents($sRealFileBase.'.json'), true) ?: array();
return \RainLoop\Model\Domain::fromArray($sName, $aDomain);
}
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.ini')) {
$aDomain = \parse_ini_file($this->sDomainPath.'/'.$sRealFileName.'.ini') ?: array();
if (\file_exists($sRealFileBase.'.ini')) {
$aDomain = \parse_ini_file($sRealFileBase.'.ini') ?: array();
return \RainLoop\Model\Domain::fromIniArray($sName, $aDomain);
}
if ($bCheckAliases && \file_exists($this->sDomainPath.'/'.$sRealFileName.'.alias')) {
$sAlias = \trim(\file_get_contents($this->sDomainPath.'/'.$sRealFileName.'.alias'));
if ($bCheckAliases && \file_exists($sRealFileBase.'.alias')) {
$sAlias = \trim(\file_get_contents($sRealFileBase.'.alias'));
if (!empty($sAlias)) {
$oDomain = $this->Load($sAlias, false, false, false);
$oDomain && $oDomain->SetAliasName($sName);
@ -116,10 +110,10 @@ class DefaultDomain implements DomainInterface
$sNames = $this->getWildcardDomainsLine();
$sFoundValue = '';
if (\strlen($sNames)
&& \RainLoop\Plugins\Helper::ValidateWildcardValues(\MailSo\Base\Utils::IdnToUtf8($sName, true), $sNames, $sFoundValue)
&& \RainLoop\Plugins\Helper::ValidateWildcardValues($sName, $sNames, $sFoundValue)
&& \strlen($sFoundValue)
) {
return $this->Load($sFoundValue, false);
return $this->Load($sFoundValue);
}
}
@ -128,111 +122,80 @@ class DefaultDomain implements DomainInterface
public function Save(\RainLoop\Model\Domain $oDomain) : bool
{
$sRealFileName = static::codeFileName($oDomain->Name());
if ($this->oCacher)
{
$this->oCacher->Delete(static::wildcardDomainsCacheKey());
}
$sRealFileName = static::encodeFileName($oDomain->Name());
\RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.json', \json_encode($oDomain, \JSON_PRETTY_PRINT));
$this->oCacher && $this->oCacher->Delete(static::CACHE_KEY);
return true;
}
public function SaveAlias(string $sName, string $sAlias) : bool
{
$sRealFileName = static::codeFileName($sName);
if ($this->oCacher)
{
$this->oCacher->Delete(static::wildcardDomainsCacheKey());
}
$sAlias = \MailSo\Base\Utils::IdnToUtf8($sAlias, true);
$sRealFileName = static::encodeFileName($sName);
\RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.alias', $sAlias);
$this->oCacher && $this->oCacher->Delete(static::CACHE_KEY);
return true;
}
protected function getDisabled() : array
{
$sFile = '';
if (\file_exists($this->sDomainPath.'/disabled')) {
$sFile = \file_get_contents($this->sDomainPath.'/disabled');
}
$aDisabled = array();
// RainLoop use comma, we use newline
$sItem = \strtok($sFile, ",\n");
while (false !== $sItem) {
$aDisabled[] = \MailSo\Base\Utils::IdnToUtf8($sItem, true);
$sItem = \strtok(",\n");
}
return $aDisabled;
// return \array_unique($aDisabled);
}
public function Disable(string $sName, bool $bDisable) : bool
{
$sName = \MailSo\Base\Utils::IdnToAscii($sName, true);
$sFile = '';
if (\file_exists($this->sDomainPath.'/disabled'))
{
$sFile = \file_get_contents($this->sDomainPath.'/disabled');
}
$aResult = array();
$aNames = \explode(',', $sFile);
if ($bDisable)
{
\array_push($aNames, $sName);
$aResult = $aNames;
}
else
{
foreach ($aNames as $sItem)
{
if ($sName !== $sItem)
{
$aResult[] = $sItem;
}
$sName = \MailSo\Base\Utils::IdnToUtf8($sName, true);
if ($sName) {
$aResult = $this->getDisabled();
if ($bDisable) {
$aResult[] = $sName;
} else {
$aResult = \array_filter($aResult, fn($v) => $v !== $sName);
}
\RainLoop\Utils::saveFile($this->sDomainPath.'/disabled', \implode("\n", \array_unique($aResult)));
}
\RainLoop\Utils::saveFile($this->sDomainPath.'/disabled', \trim(\implode(',', \array_unique($aResult)), ', '));
return true;
}
public function Delete(string $sName) : bool
{
$bResult = true;
$sRealFileName = static::codeFileName($sName);
if (\strlen($sName))
{
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.json'))
{
$bResult = 0 < \strlen($sName);
if ($bResult) {
$sRealFileName = static::encodeFileName($sName);
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.json')) {
$bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.json');
}
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.ini'))
{
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.ini')) {
$bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.ini');
}
else if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.alias'))
{
if (\file_exists($this->sDomainPath.'/'.$sRealFileName.'.alias')) {
$bResult = \unlink($this->sDomainPath.'/'.$sRealFileName.'.alias');
}
if ($bResult) {
$this->Disable($sName, false);
}
if ($this->oCacher) {
$this->oCacher->Delete(static::CACHE_KEY);
}
}
if ($bResult)
{
$this->Disable($sName, false);
}
if ($this->oCacher)
{
$this->oCacher->Delete(static::wildcardDomainsCacheKey());
}
return $bResult;
}
public function GetList(bool $bIncludeAliases = true) : array
{
$aDisabledNames = array();
if (\file_exists($this->sDomainPath.'/disabled')) {
$sDisabled = \file_get_contents($this->sDomainPath.'/disabled');
if (\strlen($sDisabled)) {
$aDisabledNames = \explode(',', $sDisabled);
foreach ($aDisabledNames as &$sValue) {
$sValue = \MailSo\Base\Utils::IdnToUtf8($sValue, true);
}
$aDisabledNames = \array_unique($aDisabledNames);
}
}
$aDisabledNames = $this->getDisabled();
$aResult = array();
$aWildCards = array();
$aAliases = array();
@ -242,7 +205,7 @@ class DefaultDomain implements DomainInterface
foreach ($aList as $sFile) {
$bAlias = '.alias' === \substr($sFile, -6);
if ($bAlias || '.json' === \substr($sFile, -5) || '.ini' === \substr($sFile, -4)) {
$sName = static::codeFileName(\preg_replace('/\.(ini|json|alias)$/', '', $sFile), true);
$sName = static::decodeFileName(\preg_replace('/\.(ini|json|alias)$/', '', $sFile));
if ($bAlias) {
if ($bIncludeAliases) {
$aAliases[$sName] = array(