Sieve filters (alpha / beta version coming soon)

This commit is contained in:
RainLoop Team 2015-01-24 02:35:42 +04:00
parent 02ba68868f
commit 94789632f0
33 changed files with 544 additions and 81 deletions

View file

@ -70,7 +70,15 @@ class ManageSieveClient extends \MailSo\Net\NetClient
*/
public function IsModuleSupported($sModule)
{
return $this->IsSupported('SIEVE') && \in_array(\strtoupper($sModule), $this->aModules);
return $this->IsSupported('SIEVE') && \in_array(\strtolower(\trim($sModule)), $this->aModules);
}
/**
* @return array
*/
public function Modules()
{
return $this->aModules;
}
/**
@ -487,7 +495,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->aAuth = \explode(' ', \strtoupper($aTokens[1]));
break;
case 'SIEVE':
$this->aModules = \explode(' ', \strtoupper($aTokens[1]));
$this->aModules = \explode(' ', \strtolower($aTokens[1]));
break;
}
}
@ -513,7 +521,6 @@ class ManageSieveClient extends \MailSo\Net\NetClient
$this->IsConnected(true);
$sRequest = \trim($sRequest);
$this->sendRaw($sRequest);
}
@ -617,7 +624,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient
*/
protected function getLogName()
{
return 'MANAGE-SIEVE';
return 'SIEVE';
}
/**

View file

@ -251,8 +251,10 @@ class Actions
case 'filters':
// \RainLoop\Providers\Filters\FiltersInterface
$oResult = new \RainLoop\Providers\Filters\SieveStorage(
!!$this->Config()->Get('labs', 'sieve_utf8_folder_name', true)
$this->Plugins(), $this->Config()
);
$oResult->SetLogger($this->Logger());
break;
case 'address-book':
// \RainLoop\Providers\AddressBook\AddressBookInterface
@ -2053,7 +2055,9 @@ class Actions
*/
public function DoFilters()
{
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Load());
$oAccount = $this->getAccountFromToken();
return $this->DefaultResponse(__FUNCTION__,
$this->FiltersProvider()->Load($oAccount, $oAccount->DomainSieveAllowRaw()));
}
/**
@ -2063,8 +2067,13 @@ class Actions
*/
public function DoFiltersSave()
{
$oAccount = $this->getAccountFromToken();
$aIncFilters = $this->GetActionParam('Filters', array());
$sRaw = $this->GetActionParam('Raw', '');
$bRawIsActive = '1' === (string) $this->GetActionParam('RawIsActive', '0');
$aFilters = array();
foreach ($aIncFilters as $aFilter)
{
@ -2078,8 +2087,8 @@ class Actions
}
}
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($aFilters));
// return $this->TrueResponse(__FUNCTION__);
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($oAccount,
$aFilters, $sRaw, $bRawIsActive));
}
/**

View file

@ -116,7 +116,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'admin_password' => array('12345'),
'allow_admin_panel' => array(true, 'Access settings'),
'allow_two_factor_auth' => array(false),
'allow_universal_login' => array(true),
'allow_universal_login' => array(false),
'admin_panel_host' => array(''),
'core_install_access_domain' => array('')
),

View file

@ -305,6 +305,38 @@ class Account extends \RainLoop\Account // for backward compatibility
return $this->Domain()->OutAuth();
}
/**
* @return string
*/
public function DomainSieveHost()
{
return $this->Domain()->SieveHost();
}
/**
* @return int
*/
public function DomainSievePort()
{
return $this->Domain()->SievePort();
}
/**
* @return int
*/
public function DomainSieveSecure()
{
return $this->Domain()->SieveSecure();
}
/**
* @return bool
*/
public function DomainSieveAllowRaw()
{
return $this->Domain()->SieveAllowRaw();
}
/**
* @return string
*/
@ -443,4 +475,52 @@ class Account extends \RainLoop\Account // for backward compatibility
return $bLogin;
}
/**
* @param \RainLoop\Plugins\Manager $oPlugins
* @param \MailSo\Sieve\ManageSieveClient $oSieveClient
* @param \RainLoop\Application $oConfig
*/
public function SieveConnectAndLoginHelper($oPlugins, $oSieveClient, $oConfig)
{
$bLogin = false;
$aSieveCredentials = array(
'UseConnect' => true,
'UseAuth' => true,
'Host' => $this->DomainSieveHost(),
'Port' => $this->DomainSievePort(),
'Secure' => $this->DomainSieveSecure(),
'Login' => $this->IncLogin(),
'Password' => $this->Password(),
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
);
$oPlugins->RunHook('filter.sieve-credentials', array($this, &$aSieveCredentials));
$oPlugins->RunHook('event.sieve-pre-connect', array($this, $aSieveCredentials['UseConnect'], $aSieveCredentials));
if ($aSieveCredentials['UseConnect'] && $oSieveClient)
{
$oSieveClient->Connect($aSieveCredentials['Host'], $aSieveCredentials['Port'],
$aSieveCredentials['Secure'], $aSieveCredentials['VerifySsl'], $aSieveCredentials['AllowSelfSigned']
);
}
$oPlugins->RunHook('event.sieve-post-connect', array($this, $aSieveCredentials['UseConnect'], $aSieveCredentials));
$oPlugins->RunHook('event.sieve-pre-login', array($this, $aSieveCredentials['UseAuth'], $aSieveCredentials));
if ($aSieveCredentials['UseAuth'])
{
$oSieveClient->Login($aSieveCredentials['Login'], $aSieveCredentials['Password']);
$bLogin = true;
}
$oPlugins->RunHook('event.sieve-post-login', array($this, $aSieveCredentials['UseAuth'], $bLogin, $aSieveCredentials));
return $bLogin;
}
}

View file

@ -83,6 +83,11 @@ class Domain
*/
private $iSieveSecure;
/**
* @var bool
*/
private $bSieveAllowRaw;
/**
* @var string
*/
@ -130,6 +135,8 @@ class Domain
$this->iSievePort = $iSievePort;
$this->iSieveSecure = $iSieveSecure;
$this->bSieveAllowRaw = false;
$this->sWhiteList = \trim($sWhiteList);
}
@ -184,6 +191,7 @@ class Domain
!empty($aDomain['imap_secure']) ? $aDomain['imap_secure'] : '');
$bUseSieve = isset($aDomain['sieve_use']) ? (bool) $aDomain['sieve_use'] : false;
$bSieveAllowRaw = isset($aDomain['sieve_allow_raw']) ? (bool) $aDomain['sieve_allow_raw'] : false;
$sSieveHost = empty($aDomain['sieve_host']) ? '' : (string) $aDomain['sieve_host'];
$iSievePort = empty($aDomain['sieve_port']) ? 2000 : (int) $aDomain['sieve_port'];
@ -207,6 +215,8 @@ class Domain
$bUseSieve, $sSieveHost, $iSievePort, $iSieveSecure,
$sOutHost, $iOutPort, $iOutSecure, $bOutShortLogin, $bOutAuth, $bOutUsePhpMail,
$sWhiteList);
$oDomain->SetSieveAllowRaw($bSieveAllowRaw);
}
return $oDomain;
@ -257,6 +267,7 @@ class Domain
'imap_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iIncSecure).'"',
'imap_short_login = '.($this->bIncShortLogin ? 'On' : 'Off'),
'sieve_use = '.($this->bUseSieve ? 'On' : 'Off'),
'sieve_allow_raw = '.($this->bSieveAllowRaw ? 'On' : 'Off'),
'sieve_host = "'.$this->encodeIniString($this->sSieveHost).'"',
'sieve_port = '.$this->iSievePort,
'sieve_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iSieveSecure).'"',
@ -427,6 +438,22 @@ class Domain
return $this->iSieveSecure;
}
/**
* @return bool
*/
public function SieveAllowRaw()
{
return $this->bSieveAllowRaw;
}
/**
* @param bool $bSieveAllowRaw
*/
public function SetSieveAllowRaw($bSieveAllowRaw)
{
$this->bSieveAllowRaw = !!$bSieveAllowRaw;
}
/**
* @return string
*/
@ -524,6 +551,7 @@ class Domain
'SieveHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->SieveHost()) : $this->SieveHost(),
'SievePort' => $this->SievePort(),
'SieveSecure' => $this->SieveSecure(),
'SieveAllowRaw' => $this->SieveAllowRaw(),
'OutHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->OutHost()) : $this->OutHost(),
'OutPort' => $this->OutPort(),
'OutSecure' => $this->OutSecure(),

View file

@ -130,6 +130,7 @@ class Domain extends \RainLoop\Providers\AbstractProvider
$iIncSecure = (int) $oActions->GetActionParam('IncSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE);
$bIncShortLogin = '1' === (string) $oActions->GetActionParam('IncShortLogin', '0');
$bUseSieve = '1' === (string) $oActions->GetActionParam('UseSieve', '0');
$bSieveAllowRaw = '1' === (string) $oActions->GetActionParam('SieveAllowRaw', '0');
$sSieveHost = (string) $oActions->GetActionParam('SieveHost', '');
$iSievePort = (int) $oActions->GetActionParam('SievePort', 2000);
$iSieveSecure = (int) $oActions->GetActionParam('SieveSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE);
@ -173,6 +174,11 @@ class Domain extends \RainLoop\Providers\AbstractProvider
$sWhiteList);
}
}
if ($oDomain)
{
$oDomain->SetSieveAllowRaw($bSieveAllowRaw);
}
}
return $oDomain;

View file

@ -18,21 +18,28 @@ class Filters extends \RainLoop\Providers\AbstractProvider
}
/**
* @param \RainLoop\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load()
public function Load($oAccount, $bAllowRaw = false)
{
return $this->IsActive() ? $this->oDriver->Load() : array();
return $this->IsActive() ? $this->oDriver->Load($oAccount, $bAllowRaw) : array();
}
/**
* @param \RainLoop\Account $oAccount
* @param array $aFilters
* @param string $sRaw = ''
* @param bool $bRawIsActive = false
*
* @return bool
*/
public function Save($aFilters)
public function Save($oAccount, $aFilters, $sRaw = '', $bRawIsActive = false)
{
return $this->IsActive() ? $this->oDriver->Save($aFilters) : false;
return $this->IsActive() ? $this->oDriver->Save($oAccount,
$aFilters, $sRaw, $bRawIsActive) : false;
}
/**

View file

@ -57,7 +57,7 @@ class Filter
/**
* @var bool
*/
private $bKeepForward;
private $bKeep;
public function __construct()
{
@ -81,7 +81,7 @@ class Filter
$this->bMarkAsRead = false;
$this->bSkipOthers = false;
$this->bKeepForward = true;
$this->bKeep = true;
}
/**
@ -167,9 +167,9 @@ class Filter
/**
* @return bool
*/
public function KeepForward()
public function Keep()
{
return $this->bKeepForward;
return $this->bKeep;
}
/**
@ -217,9 +217,9 @@ class Filter
$this->sActionValue = isset($aFilter['ActionValue']) ? $aFilter['ActionValue'] : '';
$this->sActionValueSecond = isset($aFilter['ActionValueSecond']) ? $aFilter['ActionValueSecond'] : '';
$this->bKeep = isset($aFilter['Keep']) ? '1' === (string) $aFilter['Keep'] : true;
$this->bMarkAsRead = isset($aFilter['MarkAsRead']) ? '1' === (string) $aFilter['MarkAsRead'] : false;
$this->bSkipOthers = isset($aFilter['SkipOthers']) ? '1' === (string) $aFilter['SkipOthers'] : false;
$this->bKeepForward = isset($aFilter['KeepForward']) ? '1' === (string) $aFilter['KeepForward'] : true;
$this->aConditions = \RainLoop\Providers\Filters\Classes\FilterCondition::CollectionFromJSON(
isset($aFilter['Conditions']) ? $aFilter['Conditions'] : array());
@ -255,6 +255,7 @@ class Filter
'ActionType' => $this->ActionType(),
'ActionValue' => $this->ActionValue(),
'ActionValueSecond' => $this->ActionValueSecond(),
'Keep' => $this->Keep(),
'MarkAsRead' => $this->MarkAsRead(),
'SkipOthers' => $this->SkipOthers()
);

View file

@ -8,5 +8,6 @@ class ActionType
const MOVE_TO = 'MoveTo';
const DISCARD = 'Discard';
const VACATION = 'Vacation';
const REJECT = 'Reject';
const FORWARD = 'Forward';
}

View file

@ -5,14 +5,18 @@ namespace RainLoop\Providers\Filters;
interface FiltersInterface
{
/**
* @param \RainLoop\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load();
public function Load($oAccount, $bAllowRaw = false);
/**
* @param \RainLoop\Account $oAccount
* @param array $aFilters
*
* @return bool
*/
public function Save($aFilters);
public function Save($oAccount, $aFilters);
}

View file

@ -4,7 +4,25 @@ namespace RainLoop\Providers\Filters;
class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
{
const NEW_LINE = "\n";
const NEW_LINE = "\r\n";
const SIEVE_FILE_NAME = 'rainloop.user';
const SIEVE_FILE_NAME_RAW = 'rainloop.raw';
/**
* @var \MailSo\Log\Logger
*/
private $oLogger;
/**
* @var \RainLoop\Plugins\Manager
*/
private $oPlugins;
/**
* @var \RainLoop\Application
*/
private $oConfig;
/**
* @var bool
@ -12,31 +30,130 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
private $bUtf8FolderName;
/**
* @param bool $bUtf8FolderName = true
*
* @return void
*/
public function __construct($bUtf8FolderName = true)
public function __construct($oPlugins, $oConfig)
{
$this->bUtf8FolderName = !!$bUtf8FolderName;
$this->oLogger = null;
$this->oPlugins = $oPlugins;
$this->oConfig = $oConfig;
$this->bUtf8FolderName = !!$this->oConfig->Get('labs', 'sieve_utf8_folder_name', true);
}
/**
* @param \RainLoop\Model\Account $oAccount
* @param bool $bAllowRaw = false
*
* @return array
*/
public function Load()
public function Load($oAccount, $bAllowRaw = false)
{
return $this->fileStringToCollection(@\file_get_contents('e:/sieve.txt'));
$sRaw = '';
$bBasicIsActive = false;
$bRawIsActive = false;
$aModules = array();
$aFilters = array();
$oSieveClient = \MailSo\Sieve\ManageSieveClient::NewInstance()->SetLogger($this->oLogger);
if ($oAccount->SieveConnectAndLoginHelper($this->oPlugins, $oSieveClient, $this->oConfig))
{
$aModules = $oSieveClient->Modules();
$aList = $oSieveClient->ListScripts();
if (\is_array($aList) && 0 < \count($aList))
{
if (isset($aList[self::SIEVE_FILE_NAME]))
{
$bBasicIsActive = !!$aList[self::SIEVE_FILE_NAME];
$sS = $oSieveClient->GetScript(self::SIEVE_FILE_NAME);
if ($sS)
{
$aFilters = $this->fileStringToCollection($sS);
}
}
if ($bAllowRaw && isset($aList[self::SIEVE_FILE_NAME_RAW]))
{
$bRawIsActive = !!$aList[self::SIEVE_FILE_NAME_RAW];
$sRaw = \trim($oSieveClient->GetScript(self::SIEVE_FILE_NAME_RAW));
}
}
$oSieveClient->LogoutAndDisconnect();
}
return array(
'RawIsAllow' => $bAllowRaw,
'RawIsActive' => $bRawIsActive,
'Raw' => $bAllowRaw ? $sRaw : '',
'Filters' => !$bBasicIsActive && !$bRawIsActive ? array() : $aFilters,
'Capa' => $bAllowRaw ? $aModules : array(),
'Modules' => array(
'redirect' => \in_array('fileinto', $aModules),
'moveto' => \in_array('fileinto', $aModules),
'reject' => \in_array('reject', $aModules),
'vacation' => \in_array('vacation', $aModules),
'markasread' => \in_array('imap4flags', $aModules) && false
)
);
}
/**
* @param \RainLoop\Model\Account $oAccount
* @param array $aFilters
* @param string $sRaw = ''
* @param bool $bRawIsActive = false
*
* @return bool
*/
public function Save($aFilters)
public function Save($oAccount, $aFilters, $sRaw = '', $bRawIsActive = false)
{
return @\file_put_contents('e:/sieve.txt', $this->collectionToFileString($aFilters));
$oSieveClient = \MailSo\Sieve\ManageSieveClient::NewInstance()->SetLogger($this->oLogger);
if ($oAccount->SieveConnectAndLoginHelper($this->oPlugins, $oSieveClient, $this->oConfig))
{
$aList = $oSieveClient->ListScripts();
$sUserFilter = $this->collectionToFileString($aFilters);
if (!empty($sUserFilter))
{
$oSieveClient->PutScript(self::SIEVE_FILE_NAME, $sUserFilter);
if (!$bRawIsActive)
{
$oSieveClient->SetActiveScript(self::SIEVE_FILE_NAME);
}
}
else if (isset($aList[self::SIEVE_FILE_NAME]))
{
$oSieveClient->DeleteScript(self::SIEVE_FILE_NAME);
}
$sRaw = \trim($sRaw);
if (!empty($sRaw))
{
$oSieveClient->PutScript(self::SIEVE_FILE_NAME_RAW, $sRaw);
if ($bRawIsActive)
{
$oSieveClient->SetActiveScript(self::SIEVE_FILE_NAME_RAW);
}
}
else if (isset($aList[self::SIEVE_FILE_NAME_RAW]))
{
$oSieveClient->DeleteScript(self::SIEVE_FILE_NAME_RAW);
}
$oSieveClient->LogoutAndDisconnect();
return true;
}
return false;
}
/**
@ -221,20 +338,31 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$aResult[] = $sTab.'# @Error (vacation): empty action value';
}
break;
case \RainLoop\Providers\Filters\Enumerations\ActionType::REJECT:
$sValue = \trim($oFilter->ActionValue());
if (0 < \strlen($sValue))
{
$aCapa['reject'] = true;
$aResult[] = $sTab.'reject "'.$this->quote($sValue).'";';
$aResult[] = $sTab.'stop;';
}
else
{
$aResult[] = $sTab.'# @Error (reject): empty action value';
}
break;
case \RainLoop\Providers\Filters\Enumerations\ActionType::FORWARD:
$sValue = $oFilter->ActionValue();
if (0 < \strlen($sValue))
{
if ($oFilter->KeepForward())
if ($oFilter->Keep())
{
$aCapa['copy'] = true;
$aResult[] = $sTab.'redirect :copy "'.$this->quote($sValue).'";';
}
else
{
$aResult[] = $sTab.'redirect "'.$this->quote($sValue).'";';
$aCapa['fileinto'] = true;
$aResult[] = $sTab.'fileinto "INBOX";';
}
$aResult[] = $sTab.'redirect "'.$this->quote($sValue).'";';
$aResult[] = $sTab.'stop;';
}
else
@ -246,8 +374,6 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$sValue = $oFilter->ActionValue();
if (0 < \strlen($sValue))
{
$aCapa['fileinto'] = true;
$sFolderName = $sValue; // utf7-imap
if ($this->bUtf8FolderName) // to utf-8
{
@ -256,6 +382,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
\MailSo\Base\Enumerations\Charset::UTF_8);
}
$aCapa['fileinto'] = true;
$aResult[] = $sTab.'fileinto "'.$this->quote($sFolderName).'";';
$aResult[] = $sTab.'stop;';
}
@ -359,4 +486,12 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
{
return \str_replace(array('\\', '"'), array('\\\\', '\\"'), \trim($sValue));
}
/**
* @param \MailSo\Log\Logger $oLogger
*/
public function SetLogger($oLogger)
{
$this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null;
}
}