A lot small fixes

This commit is contained in:
RainLoop Team 2015-06-04 22:02:31 +04:00
parent 4457cdbc23
commit 09334159c2
38 changed files with 1496 additions and 239 deletions

View file

@ -4127,7 +4127,7 @@ class Actions
*/
private function rainLoopRepo()
{
$sUrl = APP_REP_PATH;
$sUrl = APP_REPOSITORY_PATH;
if ('' !== $sUrl)
{
$sUrl = rtrim($sUrl, '\\/').'/';
@ -7190,12 +7190,25 @@ class Actions
$sFromFolder = $this->GetActionParam('FromFolder', '');
$sToFolder = $this->GetActionParam('ToFolder', '');
$aUids = \explode(',', (string) $this->GetActionParam('Uids', ''));
$bMarkAsRead = '1' === (string) $this->GetActionParam('MarkAsRead', '0');
$aFilteredUids = \array_filter($aUids, function (&$mUid) {
$mUid = (int) \trim($mUid);
return 0 < $mUid;
});
if ($bMarkAsRead)
{
try
{
$this->MailClient()->MessageSetSeen($sFromFolder, $aFilteredUids, true, true);
}
catch (\Exception $oException)
{
unset($oException);
}
}
try
{
$this->MailClient()->MessageMove($sFromFolder, $sToFolder, $aFilteredUids, true,
@ -7259,7 +7272,7 @@ class Actions
public function MainClearFileName($sFileName, $sContentType, $sMimeIndex, $iMaxLength = 250)
{
$sFileName = 0 === \strlen($sFileName) ? \preg_replace('/[^a-zA-Z0-9]/', '.', (empty($sMimeIndex) ? '' : $sMimeIndex.'.').$sContentType) : $sFileName;
$sClearedFileName = \preg_replace('/[\s]+/', ' ', \preg_replace('/[\.]+/', '.', $sFileName));
$sClearedFileName = \MailSo\Base\Utils::StripSpaces(\preg_replace('/[\.]+/', '.', $sFileName));
$sExt = \MailSo\Base\Utils::GetFileExtension($sClearedFileName);
if (10 < $iMaxLength && $iMaxLength < \strlen($sClearedFileName) - \strlen($sExt))

View file

@ -32,7 +32,7 @@ class Api
if ($bOne)
{
\RainLoop\Api::SetupDefaultMailSoConfig();
$bOne = \RainLoop\Api::RunResult();
}
}
@ -95,6 +95,9 @@ class Api
\MailSo\Config::$MessageListPermanentFilter =
\trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', ''));
\MailSo\Config::$MessageAllHeaders =
!!\RainLoop\Api::Config()->Get('labs', 'imap_message_all_headers', false);
\MailSo\Config::$LargeThreadLimit =
(int) \RainLoop\Api::Config()->Get('labs', 'imap_large_thread_limit', 50);

View file

@ -14,6 +14,11 @@ abstract class PdoAbstract
*/
protected $bExplain = false;
/**
* @var bool
*/
protected $bSqliteCollate = true;
/**
* @var \MailSo\Log\Logger
*/
@ -48,6 +53,18 @@ abstract class PdoAbstract
return array('', '', '', '');
}
/**
* @param string $sStr1
* @param string $sStr2
*
* @return int
*/
public function sqliteNoCaseCollationHelper($sStr1, $sStr2)
{
$this->oLogger->WriteDump(array($sStr1, $sStr2));
return \strcmp(\mb_strtoupper($sStr1, 'UTF-8'), \mb_strtoupper($sStr2, 'UTF-8'));
}
/**
* @return \PDO
*
@ -67,6 +84,7 @@ abstract class PdoAbstract
$sType = $sDsn = $sDbLogin = $sDbPassword = '';
list($sType, $sDsn, $sDbLogin, $sDbPassword) = $this->getPdoAccessData();
if (!\in_array($sType, array('mysql', 'sqlite', 'pgsql')))
{
throw new \Exception('Unknown PDO SQL connection type');
@ -82,15 +100,28 @@ abstract class PdoAbstract
$oPdo = false;
try
{
// $bCaseFunc = false;
$oPdo = @new \PDO($sDsn, $sDbLogin, $sDbPassword);
if ($oPdo)
{
$sPdoType = $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
$oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
if ('mysql' === $sType && 'mysql' === $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME))
if ('mysql' === $sType && 'mysql' === $sPdoType)
{
$oPdo->exec('SET NAMES utf8 COLLATE utf8_general_ci');
// $oPdo->exec('SET NAMES utf8');
}
// else if ('sqlite' === $sType && 'sqlite' === $sPdoType && $this->bSqliteCollate)
// {
// if (\method_exists($oPdo, 'sqliteCreateCollation') && \MailSo\Base\Utils::FunctionExistsAndEnabled('mb_strtoupper'))
// {
// $oPdo->sqliteCreateCollation('SQLITE_NOCASE_UTF8', array($this, 'sqliteNoCaseCollationHelper'));
// $bCaseFunc = true;
// }
// }
//
// $this->oLogger->Write('PDO:'.$sPdoType.($bCaseFunc ? '/SQLITE_NOCASE_UTF8' : ''));
}
}
catch (\Exception $oException)
@ -156,10 +187,11 @@ abstract class PdoAbstract
* @param string $sSql
* @param array $aParams
* @param bool $bMultiplyParams = false
* @param bool $bLogParams = false
*
* @return \PDOStatement|null
*/
protected function prepareAndExecute($sSql, $aParams = array(), $bMultiplyParams = false)
protected function prepareAndExecute($sSql, $aParams = array(), $bMultiplyParams = false, $bLogParams = false)
{
if ($this->bExplain && !$bMultiplyParams)
{
@ -172,16 +204,27 @@ abstract class PdoAbstract
$oStmt = $this->getPDO()->prepare($sSql);
if ($oStmt)
{
$aLogs = array();
$aRootParams = $bMultiplyParams ? $aParams : array($aParams);
foreach ($aRootParams as $aSubParams)
{
foreach ($aSubParams as $sName => $aValue)
{
if ($bLogParams)
{
$aLogs[$sName] = $aValue[0];
}
$oStmt->bindValue($sName, $aValue[0], $aValue[1]);
}
$mResult = $oStmt->execute() && !$bMultiplyParams ? $oStmt : null;
}
if ($bLogParams && $aLogs)
{
$this->writeLog('Params: '.@\json_encode($aLogs, \defined('JSON_UNESCAPED_UNICODE') ? JSON_UNESCAPED_UNICODE : 0));
}
}
return $mResult;

View file

@ -9,6 +9,11 @@ abstract class AbstractConfig
*/
private $sFile;
/**
* @var string
*/
private $sAdditionalFile;
/**
* @var array
*/
@ -27,12 +32,19 @@ abstract class AbstractConfig
/**
* @param string $sFileName
* @param string $sFileHeader = ''
* @param string $sAdditionalFileName = ''
*
* @return void
*/
public function __construct($sFileName, $sFileHeader = '')
public function __construct($sFileName, $sFileHeader = '', $sAdditionalFileName = '')
{
$this->sFile = \APP_PRIVATE_DATA.'configs/'.$sFileName;
$this->sFile = \APP_PRIVATE_DATA.'configs/'.\trim($sFileName);
$sAdditionalFileName = \trim($sAdditionalFileName);
$this->sAdditionalFile = \APP_PRIVATE_DATA.'configs/'.$sAdditionalFileName;
$this->sAdditionalFile = 0 < \strlen($sAdditionalFileName) &&
\file_exists($this->sAdditionalFile) ? $this->sAdditionalFile : '';
$this->sFileHeader = $sFileHeader;
$this->aData = $this->defaultValues();
@ -109,7 +121,7 @@ abstract class AbstractConfig
*/
private function cacheKey()
{
return 'config:'.\sha1($this->sFile).':';
return 'config:'.\sha1($this->sFile).':'.\sha1($this->sAdditionalFile).':';
}
/**
@ -120,12 +132,17 @@ abstract class AbstractConfig
if ($this->bUseApcCache)
{
$iMTime = @\filemtime($this->sFile);
if (\is_int($iMTime) && 0 < $iMTime)
$iMTime = \is_int($iMTime) && 0 < $iMTime ? $iMTime : 0;
$iATime = $this->sAdditionalFile ? @\filemtime($this->sAdditionalFile) : 0;
$iATime = \is_int($iATime) && 0 < $iATime ? $iATime : 0;
if (0 < $iMTime)
{
$sKey = $this->cacheKey();
$iTime = \apc_fetch($sKey.'time');
if ($iTime && $iMTime === (int) $iTime)
$sTimeHash = \apc_fetch($sKey.'time');
if ($sTimeHash && $sTimeHash === \md5($iMTime.'/'.$iATime))
{
$aFetchData = \apc_fetch($sKey.'data');
if (\is_array($aFetchData))
@ -148,11 +165,16 @@ abstract class AbstractConfig
if ($this->bUseApcCache)
{
$iMTime = @\filemtime($this->sFile);
if (\is_int($iMTime) && 0 < $iMTime)
$iMTime = \is_int($iMTime) && 0 < $iMTime ? $iMTime : 0;
$iATime = $this->sAdditionalFile ? @\filemtime($this->sAdditionalFile) : 0;
$iATime = \is_int($iATime) && 0 < $iATime ? $iATime : 0;
if (0 < $iMTime)
{
$sKey = $this->cacheKey();
\apc_store($sKey.'time', $iMTime);
\apc_store($sKey.'time', \md5($iMTime.'/'.$iATime));
\apc_store($sKey.'data', $this->aData);
return true;
@ -193,7 +215,7 @@ abstract class AbstractConfig
}
$aData = \RainLoop\Utils::CustomParseIniFile($this->sFile, true);
if (\is_array($aData) && 0 < count($aData))
if (\is_array($aData) && 0 < \count($aData))
{
foreach ($aData as $sSectionKey => $aSectionValue)
{
@ -206,6 +228,28 @@ abstract class AbstractConfig
}
}
unset($aData);
if (\file_exists($this->sAdditionalFile) && \is_readable($this->sAdditionalFile))
{
$aSubData = \RainLoop\Utils::CustomParseIniFile($this->sAdditionalFile, true);
if (\is_array($aSubData) && 0 < \count($aSubData))
{
foreach ($aSubData as $sSectionKey => $aSectionValue)
{
if (\is_array($aSectionValue))
{
foreach ($aSectionValue as $sParamKey => $mParamValue)
{
$this->Set($sSectionKey, $sParamKey, $mParamValue);
}
}
}
}
unset($aSubData);
}
$this->storeDataToCache();
return true;

View file

@ -11,7 +11,8 @@ class Application extends \RainLoop\Config\AbstractConfig
{
parent::__construct('application.ini',
'; RainLoop Webmail configuration file
; Please don\'t add custom parameters here, those will be overwritten');
; Please don\'t add custom parameters here, those will be overwritten',
defined('APP_ADDITIONAL_CONFIGURATION_NAME') ? APP_ADDITIONAL_CONFIGURATION_NAME : '');
}
/**
@ -321,6 +322,7 @@ Enables caching in the system'),
'imap_message_list_count_limit_trigger' => array(0),
'imap_message_list_date_filter' => array(0),
'imap_message_list_permanent_filter' => array(''),
'imap_message_all_headers' => array(false),
'imap_large_thread_limit' => array(50),
'imap_folder_list_limit' => array(200),
'imap_show_login_alert' => array(true),

View file

@ -25,7 +25,7 @@ class Plugin extends \RainLoop\Config\AbstractConfig
*/
private function convertConfigMap($aMap)
{
if (0 < count($aMap))
if (0 < \count($aMap))
{
$aResultMap = array();
foreach ($aMap as /* @var $oProperty \RainLoop\Plugins\Property */ $oProperty)
@ -33,12 +33,12 @@ class Plugin extends \RainLoop\Config\AbstractConfig
if ($oProperty)
{
$mValue = $oProperty->DefaultValue();
$sValue = is_array($mValue) && isset($mValue[0]) ? $mValue[0] : $mValue;
$sValue = \is_array($mValue) && isset($mValue[0]) ? $mValue[0] : $mValue;
$aResultMap[$oProperty->Name()] = array($sValue, '');
}
}
if (0 < count($aResultMap))
if (0 < \count($aResultMap))
{
return array(
'plugin' => $aResultMap

View file

@ -26,6 +26,11 @@ class Property
*/
public $Value;
/**
* @var string
*/
public $ValueLower;
/**
* @var string
*/
@ -54,6 +59,7 @@ class Property
$this->TypeStr = '';
$this->Value = '';
$this->ValueLower = '';
$this->ValueCustom = '';
$this->Frec = 0;
@ -120,6 +126,7 @@ class Property
$this->Value = \trim($this->Value);
$this->ValueCustom = \trim($this->ValueCustom);
$this->TypeStr = \trim($this->TypeStr);
$this->ValueLower = '';
if (0 < \strlen($this->Value))
{
@ -131,16 +138,22 @@ class Property
if ($this->IsName())
{
$this->Value = \preg_replace('/[\s]+/u', ' ', $this->Value);
$this->Value = \MailSo\Base\Utils::StripSpaces($this->Value);
}
// phones clear value for searching
// lower value for searching
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('mb_strtolower'))
{
$this->ValueLower = (string) @\mb_strtolower($this->Value, 'UTF-8');
}
// phone value for searching
if ($this->IsPhone())
{
$sPhone = $this->Value;
$sPhone = \trim($this->Value);
$sPhone = \preg_replace('/^[+]+/', '', $sPhone);
$sPhone = \preg_replace('/[^\d]/', '', $sPhone);
$this->ValueCustom = $sPhone;
$this->ValueCustom = \trim($sPhone);
}
}
}

View file

@ -957,6 +957,7 @@ class PdoAddressBook
':prop_type' => array($oProp->Type, \PDO::PARAM_INT),
':prop_type_str' => array($oProp->TypeStr, \PDO::PARAM_STR),
':prop_value' => array($oProp->Value, \PDO::PARAM_STR),
':prop_value_lower' => array($oProp->ValueLower, \PDO::PARAM_STR),
':prop_value_custom' => array($oProp->ValueCustom, \PDO::PARAM_STR),
':prop_frec' => array($iFreq, \PDO::PARAM_INT),
);
@ -965,9 +966,9 @@ class PdoAddressBook
if (0 < \count($aParams))
{
$sSql = 'INSERT INTO rainloop_ab_properties '.
'( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_custom, prop_frec)'.
'( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_lower, prop_value_custom, prop_frec)'.
' VALUES '.
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)';
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_lower, :prop_value_custom, :prop_frec)';
$this->prepareAndExecute($sSql, $aParams, true);
}
@ -1072,6 +1073,7 @@ class PdoAddressBook
if (0 < \strlen($sSearch))
{
$sCustomSearch = $this->specialConvertSearchValueCustomPhone($sSearch);
$sLowerSearch = $this->specialConvertSearchValueLower($sSearch, '=');
$sSearchTypes = \implode(',', array(
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME,
@ -1079,8 +1081,10 @@ class PdoAddressBook
));
$sSql = 'SELECT id_user, id_prop, id_contact FROM rainloop_ab_properties '.
'WHERE (id_user = :id_user) AND prop_type IN ('.$sSearchTypes.') AND (prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sCustomSearch) ? ' OR (prop_type = '.PropertyType::PHONE.' AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
'WHERE (id_user = :id_user) AND prop_type IN ('.$sSearchTypes.') AND ('.
'prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sLowerSearch) ? ' OR (prop_value_lower <> \'\' AND prop_value_lower LIKE :search_lower ESCAPE \'=\')' : '').
(0 < \strlen($sCustomSearch) ? ' OR (prop_type = '.PropertyType::PHONE.' AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
') GROUP BY id_contact, id_prop';
$aParams = array(
@ -1088,12 +1092,17 @@ class PdoAddressBook
':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR)
);
if (0 < \strlen($sLowerSearch))
{
$aParams[':search_lower'] = array($sLowerSearch, \PDO::PARAM_STR);
}
if (0 < \strlen($sCustomSearch))
{
$aParams[':search_custom_phone'] = array($sCustomSearch, \PDO::PARAM_STR);
}
$oStmt = $this->prepareAndExecute($sSql, $aParams);
$oStmt = $this->prepareAndExecute($sSql, $aParams, false, true);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
@ -1211,6 +1220,7 @@ class PdoAddressBook
$oProperty->Type = (int) $aItem['prop_type'];
$oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : '';
$oProperty->Value = (string) $aItem['prop_value'];
$oProperty->ValueLower = isset($aItem['prop_value_lower']) ? (string) $aItem['prop_value_lower'] : '';
$oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : '';
$oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0;
@ -1320,6 +1330,7 @@ class PdoAddressBook
$oProperty->Type = (int) $aItem['prop_type'];
$oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : '';
$oProperty->Value = (string) $aItem['prop_value'];
$oProperty->ValueLower = isset($aItem['prop_value_lower']) ? (string) $aItem['prop_value_lower'] : '';
$oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : '';
$oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0;
@ -1364,8 +1375,14 @@ class PdoAddressBook
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME
));
$sLowerSearch = $this->specialConvertSearchValueLower($sSearch);
$sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_ab_properties '.
'WHERE (id_user = :id_user) AND prop_type IN ('.$sTypes.') AND prop_value LIKE :search ESCAPE \'=\'';
'WHERE (id_user = :id_user) AND prop_type IN ('.$sTypes.') AND ('.
'prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sLowerSearch) ? ' OR (prop_value_lower <> \'\' AND prop_value_lower LIKE :search_lower ESCAPE \'=\')' : '').
')'
;
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
@ -1373,6 +1390,11 @@ class PdoAddressBook
':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR)
);
if (0 < \strlen($sLowerSearch))
{
$aParams[':search_lower'] = array($sLowerSearch, \PDO::PARAM_STR);
}
$sSql .= ' ORDER BY prop_frec DESC';
$sSql .= ' LIMIT :limit';
@ -1547,7 +1569,7 @@ class PdoAddressBook
{
$oResult = \MailSo\Mime\Email::Parse(\trim($mItem));
}
catch (\Exception $oException) {}
catch (\Exception $oException) { unset($oException); }
return $oResult;
}, $aEmails);
@ -1837,7 +1859,7 @@ SQLITEINITIAL;
break;
}
if (0 < strlen($sInitial))
if (0 < \strlen($sInitial))
{
$aList = \explode(';', \trim($sInitial));
foreach ($aList as $sV)
@ -1869,17 +1891,26 @@ SQLITEINITIAL;
{
case 'mysql':
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_ab_properties ADD prop_value_lower varchar(255) NOT NULL DEFAULT \'\' AFTER prop_value_custom;'
)
));
break;
case 'pgsql':
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_ab_properties ADD prop_value_lower text NOT NULL DEFAULT \'\';'
)
));
break;
case 'sqlite':
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_ab_properties ADD prop_value_lower text NOT NULL DEFAULT \'\';'
)
));
break;
}
@ -1934,6 +1965,24 @@ SQLITEINITIAL;
array($sEscapeSign.$sEscapeSign, $sEscapeSign.'_', $sEscapeSign.'%'), $sSearch).'%';
}
/**
* @param string $sSearch
* @param string $sEscapeSign = '='
*
* @return string
*/
private function specialConvertSearchValueLower($sSearch, $sEscapeSign = '=')
{
if (!\MailSo\Base\Utils::FunctionExistsAndEnabled('mb_strtolower'))
{
return '';
}
return '%'.\str_replace(array($sEscapeSign, '_', '%'),
array($sEscapeSign.$sEscapeSign, $sEscapeSign.'_', $sEscapeSign.'%'),
(string) @\mb_strtolower($sSearch, 'UTF-8')).'%';
}
/**
* @param string $sSearch
*

View file

@ -242,7 +242,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$sResult .= ' "'.$this->quote($sValue).'"';
}
$sResult = \preg_replace('/[\s]+/u', ' ', $sResult);
$sResult = \MailSo\Base\Utils::StripSpaces($sResult);
}
}
else
@ -358,8 +358,8 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface
$sSubject = '';
if (0 < \strlen($sValueSecond))
{
$sSubject = ':subject "'.$this->quote(
\preg_replace('/[\s]+/u', ' ', $sValueSecond)).'" ';
$sSubject = ':subject "'.
$this->quote(\MailSo\Base\Utils::StripSpaces($sValueSecond)).'" ';
}
if (0 < \strlen($sValueThird) && \is_numeric($sValueThird) && 1 < (int) $sValueThird)