mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-02-06 14:04:24 +08:00
Original unminified source code (dev folder - js, css, less) (fixes #6) Grunt build system Multiple identities correction (fixes #9) Compose html editor (fixes #12) New general settings - Loading Description New warning about default admin password Split general and login screen settings
600 lines
No EOL
15 KiB
PHP
600 lines
No EOL
15 KiB
PHP
<?php
|
|
|
|
namespace RainLoop\Providers\Contacts;
|
|
|
|
class DefaultContacts implements \RainLoop\Providers\Contacts\ContactsInterface
|
|
{
|
|
/**
|
|
* @var \MailSo\Log\Logger
|
|
*/
|
|
private $oLogger;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $bUseDbPerUser;
|
|
|
|
/**
|
|
* @param \MailSo\Log\Logger $oLogger = null
|
|
*/
|
|
public function __construct($oLogger = null)
|
|
{
|
|
$this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null;
|
|
$this->bUseDbPerUser = true;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @staticvar array $aPdoCache
|
|
* @return \PDO
|
|
*/
|
|
private function getPDO($oAccount)
|
|
{
|
|
static $aPdoCache = array();
|
|
|
|
$sEmail = $oAccount->ParentEmailHelper();
|
|
if (isset($aPdoCache[$sEmail]))
|
|
{
|
|
return $aPdoCache[$sEmail];
|
|
}
|
|
|
|
if (!\class_exists('PDO'))
|
|
{
|
|
throw new \Exception('class_exists=PDO');
|
|
}
|
|
|
|
$sVersionFile = '';
|
|
$sDsn = '';
|
|
|
|
if ($this->bUseDbPerUser)
|
|
{
|
|
$sSubPath = APP_PRIVATE_DATA.'storage/contacts/'.\rtrim(\substr($sEmail, 0, 2), '@').'/'.$sEmail.'/';
|
|
if (!\is_dir($sSubPath))
|
|
{
|
|
if (\mkdir($sSubPath, 0755, true) && \is_dir($sSubPath))
|
|
{
|
|
$sVersionFile = $sSubPath.'.version';
|
|
$sDsn = 'sqlite:'.$sSubPath.'contacts.sqlite';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$sVersionFile = $sSubPath.'.version';
|
|
$sDsn = 'sqlite:'.$sSubPath.'contacts.sqlite';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$sVersionFile = APP_PRIVATE_DATA.'.version';
|
|
$sDsn = 'sqlite:'.APP_PRIVATE_DATA.'contacts.sqlite';
|
|
}
|
|
|
|
$sDbLogin = '';
|
|
$sDbPassword = '';
|
|
|
|
$oPdo = false;
|
|
try
|
|
{
|
|
$oPdo = new \PDO($sDsn, $sDbLogin, $sDbPassword);
|
|
if ($oPdo)
|
|
{
|
|
$oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
|
|
|
if (!@\file_exists($sVersionFile) ||
|
|
(string) @file_get_contents($sVersionFile) !== (string) \RainLoop\Providers\Contacts\Classes\Db::Version())
|
|
{
|
|
$this->syncTables($oPdo, $sVersionFile);
|
|
}
|
|
|
|
$oPdo->sqliteCreateFunction('SIMPLESEARCH', function ($sEmailValue, $sNameValue, $sMask) {
|
|
return \preg_match('/'.\preg_quote($sMask, '/').'/ui',
|
|
$sEmailValue.' '.$sNameValue) ? 1 : 0;
|
|
});
|
|
|
|
}
|
|
}
|
|
catch (\Exception $oException)
|
|
{
|
|
throw $oException;
|
|
$oPdo = false;
|
|
}
|
|
|
|
if ($oPdo)
|
|
{
|
|
$aPdoCache[$oAccount->ParentEmailHelper()] = $oPdo;
|
|
}
|
|
|
|
return $oPdo;
|
|
}
|
|
|
|
protected function getWantedColumnsInfo($sTableName, $aCurrent)
|
|
{
|
|
$aColumns = array();
|
|
foreach ($aCurrent as $sName => $sType)
|
|
{
|
|
$sSql = $sName.' '.$sType;
|
|
switch ($sType)
|
|
{
|
|
case 'INTEGER':
|
|
$sSql .= ' DEFAILT \'0\'';
|
|
break;
|
|
case 'TEXT':
|
|
$sSql .= ' DEFAILT \'\'';
|
|
break;
|
|
}
|
|
|
|
$aColumns[$sName] = $sSql;
|
|
}
|
|
|
|
if (0 === \count($aColumns))
|
|
{
|
|
return '';
|
|
}
|
|
|
|
return 'CREATE TABLE '.$sTableName.' (' .\implode(', ', $aColumns).')';
|
|
}
|
|
|
|
/**
|
|
* @param \PDO $oAccount
|
|
* @param string $sTableName
|
|
*/
|
|
protected function getCurrentColumnsInfo($oPdo, $sTableName)
|
|
{
|
|
$oStmt = $this->prepareAndExecute($oPdo, 'SELECT `sql` FROM `sqlite_master` WHERE `tbl_name` = :TableName AND `type` = :Type', array(
|
|
':TableName' => array($sTableName, \PDO::PARAM_STR),
|
|
':Type' => array('table', \PDO::PARAM_STR)
|
|
));
|
|
|
|
if ($oStmt)
|
|
{
|
|
$mRow = $oStmt->fetch(\PDO::FETCH_ASSOC);
|
|
if ($mRow && isset($mRow['sql']))
|
|
{
|
|
return (string) $mRow['sql'];
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @param \PDO $oPdo
|
|
* @param string $sVersionFile = ''
|
|
*/
|
|
private function syncTables($oPdo, $sVersionFile = '')
|
|
{
|
|
if ($this->oLogger)
|
|
{
|
|
$this->oLogger->Write('Start to sync', \MailSo\Log\Enumerations\Type::INFO);
|
|
}
|
|
|
|
$aStrucure = \RainLoop\Providers\Contacts\Classes\Db::Strucure();
|
|
foreach ($aStrucure as $sTableName => $aField)
|
|
{
|
|
$sCurrent = $this->getCurrentColumnsInfo($oPdo, $sTableName);
|
|
$sWanted = $this->getWantedColumnsInfo($sTableName, $aField);
|
|
|
|
if (empty($sCurrent))
|
|
{
|
|
$this->prepareAndExecute($oPdo, $sWanted);
|
|
}
|
|
else if ($sCurrent !== $sWanted)
|
|
{
|
|
$this->prepareAndExecute($oPdo, 'DROP TABLE IF EXISTS `'.$sTableName.'_old`');
|
|
$this->prepareAndExecute($oPdo, 'ALTER TABLE `'.$sTableName.'` RENAME TO `'.$sTableName.'_old`');
|
|
$this->prepareAndExecute($oPdo, $sWanted);
|
|
|
|
$aNewKeys = array();
|
|
$aOldKeys = array();
|
|
foreach ($aField as $sKey => $sType)
|
|
{
|
|
$aNewKeys[] = $sKey;
|
|
$aOldKeys[] = false !== \strpos($sCurrent, $sKey.' ') ? $sKey :
|
|
(0 === \strpos($sType, 'INT') ? '\'0\'' : '\'\'');
|
|
}
|
|
|
|
$sNewKeys = \implode(', ', $aNewKeys);
|
|
$sOldKeys = \implode(', ', $aOldKeys);
|
|
$this->prepareAndExecute($oPdo, 'INSERT INTO `'.$sTableName.'` ('.$sNewKeys.') SELECT '.$sOldKeys.' FROM `'.$sTableName.'_old`');
|
|
|
|
$this->prepareAndExecute($oPdo, 'DROP TABLE `'.$sTableName.'_old`');
|
|
}
|
|
}
|
|
|
|
@\file_put_contents($sVersionFile, \RainLoop\Providers\Contacts\Classes\Db::Version());
|
|
|
|
if ($this->oLogger)
|
|
{
|
|
$this->oLogger->Write('Stop to sync', \MailSo\Log\Enumerations\Type::INFO);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account|\PDO $oAccountOrPdo
|
|
* @param string $sSql
|
|
* @param array $aParams
|
|
*
|
|
* @return \PDOStatement|null
|
|
*/
|
|
private function prepareAndExecute($oAccountOrPdo, $sSql, $aParams = array())
|
|
{
|
|
if ($this->oLogger)
|
|
{
|
|
$this->oLogger->Write($sSql, \MailSo\Log\Enumerations\Type::INFO, 'SQLITE');
|
|
}
|
|
|
|
$oPdo = $oAccountOrPdo instanceof \PDO ? $oAccountOrPdo : $this->getPDO($oAccountOrPdo);
|
|
$oStmt = $oPdo->prepare($sSql);
|
|
foreach ($aParams as $sName => $aValue)
|
|
{
|
|
$oStmt->bindValue($sName, $aValue[0], $aValue[1]);
|
|
}
|
|
|
|
// $sLogSql = $sSql;
|
|
// foreach($aParams as $sName => $aValue)
|
|
// {
|
|
// $sLogSql = \str_replace($sName, $aValue[1] === \PDO::PARAM_INT ? $aValue[0] : '\''.$aValue[0].'\'', $sLogSql);
|
|
// }
|
|
//
|
|
// if ($this->oLogger)
|
|
// {
|
|
// $this->oLogger->Write($sLogSql, \MailSo\Log\Enumerations\Type::INFO, 'SQLITE');
|
|
// }
|
|
|
|
$mResult = $oStmt->execute() ? $oStmt : null;
|
|
|
|
// if ($this->oLogger)
|
|
// {
|
|
// $this->oLogger->Write('RESULT: '.($mResult ? 'true' : 'false'), \MailSo\Log\Enumerations\Type::INFO, 'SQLITE');
|
|
// }
|
|
|
|
return $mResult;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account|null $oAccount
|
|
* @param bool $bSkipInsert = false
|
|
*
|
|
* @return int
|
|
*/
|
|
private function getUserId($oAccount, $bSkipInsert = false)
|
|
{
|
|
if (!$this->bUseDbPerUser)
|
|
{
|
|
$oStmt = $this->prepareAndExecute($oAccount,
|
|
'SELECT IdUser FROM rlContactsUsers WHERE Email = :Email LIMIT 1',
|
|
array(
|
|
':Email' => array($oAccount->ParentEmailHelper(), \PDO::PARAM_STR)
|
|
));
|
|
|
|
$mRow = $oStmt->fetch(\PDO::FETCH_ASSOC);
|
|
if ($mRow && isset($mRow['IdUser']) && \is_numeric($mRow['IdUser']))
|
|
{
|
|
return (int) $mRow['IdUser'];
|
|
}
|
|
|
|
if (!$bSkipInsert)
|
|
{
|
|
$oStmt->closeCursor();
|
|
|
|
$oStmt = $this->prepareAndExecute($oAccount,
|
|
'INSERT INTO rlContactsUsers (Email) VALUES (:Email)',
|
|
array(
|
|
':Email' => array($oAccount->ParentEmailHelper(), \PDO::PARAM_STR)
|
|
));
|
|
|
|
return $this->getUserId($oAccount, true);
|
|
}
|
|
|
|
throw new \Exception('IdUser=0');
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param string $sSearch
|
|
* @return string
|
|
*/
|
|
private function convertSearchValue($sSearch)
|
|
{
|
|
return '%'.$sSearch.'%';
|
|
}
|
|
|
|
private function populateContactFromDB($iUserID, $aItem)
|
|
{
|
|
$oContact = null;
|
|
if (isset($aItem['IdContact']))
|
|
{
|
|
$oContact = new \RainLoop\Providers\Contacts\Classes\Contact();
|
|
$oContact->IdContact = (int) $aItem['IdContact'];
|
|
$oContact->IdUser = $iUserID;
|
|
$oContact->Type = (int) $aItem['Type'];
|
|
$oContact->Frec = (int) $aItem['Frec'];
|
|
$oContact->ListName = (string) $aItem['ListName'];
|
|
$oContact->Name = (string) $aItem['Name'];
|
|
$oContact->Emails = \explode(' ', \trim((string) $aItem['Emails']));
|
|
$oContact->ImageHash = (string) $aItem['ImageHash'];
|
|
|
|
$oContact->ParseData($aItem['Data']);
|
|
}
|
|
|
|
return $oContact;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param int $iIdContact
|
|
*
|
|
* @return $oContact|null
|
|
*/
|
|
public function GetContactById($oAccount, $iIdContact)
|
|
{
|
|
$oResultContact = null;
|
|
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$oStmt = $this->prepareAndExecute($oAccount,
|
|
'SELECT IdContact, Type, Frec, ListName, Name, Emails, ImageHash, Data'.
|
|
' FROM rlContactsItems WHERE IdContact = :IdContact AND IdUser = :IdUser LIMIT 1',
|
|
array(
|
|
':IdContact' => array($iIdContact, \PDO::PARAM_INT),
|
|
':IdUser' => array($iUserID, \PDO::PARAM_INT)
|
|
));
|
|
|
|
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
if (\is_array($aFetch) && isset($aFetch[0]) && \is_array($aFetch[0]))
|
|
{
|
|
$oContact = $this->populateContactFromDB($iUserID, $aFetch[0]);
|
|
if ($oContact instanceof \RainLoop\Providers\Contacts\Classes\Contact)
|
|
{
|
|
$oResultContact = $oContact;
|
|
}
|
|
}
|
|
|
|
return $oResultContact;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param int $iOffset = 0
|
|
* @param int $iLimit = 20
|
|
* @param string $sSearch = ''
|
|
*
|
|
* @return array
|
|
*/
|
|
public function GetContacts($oAccount, $iOffset = 0, $iLimit = 20, $sSearch = '')
|
|
{
|
|
$iOffset = 0 <= $iOffset ? $iOffset : 0;
|
|
$iLimit = 0 < $iLimit ? (int) $iLimit : 0;
|
|
$sSearch = \trim($sSearch);
|
|
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$sSql = 'SELECT IdContact, Type, Frec, ListName, Name, Emails, ImageHash, Data'.
|
|
' FROM rlContactsItems WHERE IdUser = :IdUser'
|
|
;
|
|
|
|
$aParams = array(
|
|
':IdUser' => array($iUserID, \PDO::PARAM_INT),
|
|
':limit' => array($iLimit, \PDO::PARAM_INT),
|
|
':offset' => array($iOffset, \PDO::PARAM_INT)
|
|
);
|
|
|
|
if (0 < strlen($sSearch))
|
|
{
|
|
if (\MailSo\Base\Utils::IsAscii($sSearch))
|
|
{
|
|
$sSql .= ' AND Name LIKE :Search OR Emails LIKE :Search';
|
|
$aParams[':Search'] = array($this->convertSearchValue($sSearch), \PDO::PARAM_STR);
|
|
}
|
|
else
|
|
{
|
|
$sSql .= ' AND SIMPLESEARCH(Emails, Name, :Search)';
|
|
$aParams[':Search'] = array($sSearch, \PDO::PARAM_STR);
|
|
}
|
|
}
|
|
|
|
$sSql .= ' ORDER BY ListName ASC LIMIT :limit OFFSET :offset';
|
|
|
|
$oStmt = $this->prepareAndExecute($oAccount, $sSql, $aParams);
|
|
|
|
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
$aResult = array();
|
|
if (\is_array($aFetch) && 0 < \count($aFetch))
|
|
{
|
|
foreach ($aFetch as $aItem)
|
|
{
|
|
$oContact = $this->populateContactFromDB($iUserID, $aItem);
|
|
if ($oContact instanceof \RainLoop\Providers\Contacts\Classes\Contact)
|
|
{
|
|
$aResult[] = $oContact;
|
|
}
|
|
}
|
|
}
|
|
|
|
unset($aFetch);
|
|
return $aResult;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
*
|
|
* @return array
|
|
*/
|
|
public function GetContactsImageHashes($oAccount)
|
|
{
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$oStmt = $this->prepareAndExecute($oAccount,
|
|
'SELECT Emails, ImageHash FROM rlContactsItems WHERE IdUser = :IdUser AND ImageHash <> :ImageHash',
|
|
array(
|
|
':IdUser' => array($iUserID, \PDO::PARAM_INT),
|
|
':ImageHash' => array('', \PDO::PARAM_STR)
|
|
));
|
|
|
|
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
|
|
$aResult = array();
|
|
|
|
if (\is_array($aFetch) && 0 < \count($aFetch))
|
|
{
|
|
foreach ($aFetch as $aItem)
|
|
{
|
|
if (!empty($aItem['Emails']) && !empty($aItem['ImageHash']))
|
|
{
|
|
$aEmails = \explode(' ', $aItem['Emails']);
|
|
foreach ($aEmails as $sEmail)
|
|
{
|
|
$sEmail = \trim($sEmail);
|
|
if (0 < strlen($sEmail))
|
|
{
|
|
$aResult[$sEmail] = $aItem['ImageHash'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unset($aFetch);
|
|
return $aResult;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param \RainLoop\Providers\Contacts\Classes\Contact $oContact
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function CreateContact($oAccount, &$oContact)
|
|
{
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$oContact->IdUser = $iUserID;
|
|
|
|
$oStmt = $this->prepareAndExecute($oAccount,
|
|
'INSERT INTO rlContactsItems '.
|
|
'( IdUser, Type, Frec, ListName, Name, Emails, ImageHash, Data) VALUES '.
|
|
'(:IdUser, :Type, 0, :ListName, :Name, :Emails, :ImageHash, :Data)',
|
|
array(
|
|
':IdUser' => array($oContact->IdUser, \PDO::PARAM_INT),
|
|
':Type' => array($oContact->Type, \PDO::PARAM_INT),
|
|
':ListName' => array($oContact->GenarateListName(), \PDO::PARAM_STR),
|
|
':Name' => array($oContact->Name, \PDO::PARAM_STR),
|
|
':Emails' => array($oContact->EmailsAsString(), \PDO::PARAM_STR),
|
|
':ImageHash' => array($oContact->ImageHash, \PDO::PARAM_STR),
|
|
':Data' => array($oContact->DataAsString(), \PDO::PARAM_STR),
|
|
));
|
|
|
|
if ($oStmt)
|
|
{
|
|
$iContactID = $this->getPDO($oAccount)->lastInsertId('IdContact');
|
|
if (is_numeric($iContactID) && 0 < (int) $iContactID)
|
|
{
|
|
$oContact->IdContact = (int) $iContactID;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
throw new \Exception('CreateContact');
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param \RainLoop\Providers\Contacts\Classes\Contact $oContact
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function UpdateContact($oAccount, &$oContact)
|
|
{
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$oContact->IdUser = $iUserID;
|
|
|
|
return !!$this->prepareAndExecute($oAccount,
|
|
'UPDATE rlContactsItems SET'.
|
|
' Type = :Type, ListName = :ListName, Name = :Name, Emails = :Emails,'.
|
|
' ImageHash = :ImageHash, Data = :Data'.
|
|
' WHERE IdContact = :IdContact AND IdUser = :IdUser',
|
|
array(
|
|
':IdContact' => array($oContact->IdContact, \PDO::PARAM_INT),
|
|
':IdUser' => array($oContact->IdUser, \PDO::PARAM_INT),
|
|
':Type' => array($oContact->Type, \PDO::PARAM_INT),
|
|
':ListName' => array($oContact->GenarateListName(), \PDO::PARAM_STR),
|
|
':Name' => array($oContact->Name, \PDO::PARAM_STR),
|
|
':Emails' => array($oContact->EmailsAsString(), \PDO::PARAM_STR),
|
|
':ImageHash' => array($oContact->ImageHash, \PDO::PARAM_STR),
|
|
':Data' => array($oContact->DataAsString(), \PDO::PARAM_STR),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param array $aContactIds
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function DeleteContacts($oAccount, $aContactIds)
|
|
{
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$aParams = array(
|
|
':IdUser' => array($iUserID, \PDO::PARAM_INT),
|
|
);
|
|
|
|
$aInQuery = array();
|
|
foreach ($aContactIds as $iIndex => $iId)
|
|
{
|
|
$aInQuery[] = ':IdContact_'.$iIndex;
|
|
$aParams[':IdContact_'.$iIndex] = array($iId, \PDO::PARAM_INT);
|
|
}
|
|
|
|
if (0 === \count($aInQuery))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !!$this->prepareAndExecute($oAccount,
|
|
'DELETE FROM rlContactsItems WHERE IdUser = :IdUser AND IdContact IN ('.\implode(', ', $aInQuery).')',
|
|
$aParams);
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function IsSupported()
|
|
{
|
|
$aDrivers = \class_exists('PDO') ? \PDO::getAvailableDrivers() : array();
|
|
return \is_array($aDrivers) ? \in_array('sqlite', $aDrivers) : false;
|
|
}
|
|
|
|
/**
|
|
* @param \RainLoop\Account $oAccount
|
|
* @param array $aContactIds
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function IncFrec($oAccount, $aContactIds)
|
|
{
|
|
if (\is_array($aContactIds) && 0 < \count($aContactIds))
|
|
{
|
|
$iUserID = $this->getUserId($oAccount);
|
|
|
|
$aParams = array(
|
|
':IdUser' => array($iUserID, \PDO::PARAM_INT),
|
|
);
|
|
|
|
$aInQuery = array();
|
|
foreach ($aContactIds as $iIndex => $iId)
|
|
{
|
|
$aInQuery[] = ':IdContact_'.$iIndex;
|
|
$aParams[':IdContact_'.$iIndex] = array($iId, \PDO::PARAM_INT);
|
|
}
|
|
|
|
return !!$this->prepareAndExecute($oAccount,
|
|
'UPDATE rlContactsItems SET Frec = Frec + 1 WHERE IdUser = :IdUser AND IdContact IN ('.\implode(', ', $aInQuery).')',
|
|
$aParams);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} |