diff --git a/dev/App/User.js b/dev/App/User.js index 332884a3a..ace9abbc6 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -494,15 +494,23 @@ AppUser.prototype.accountsCounts = function () { + AccountStore.accounts.loading(true); + Remote.accountsCounts(function (sResult, oData) { + + AccountStore.accounts.loading(false); + if (Enums.StorageResultType.Success === sResult && oData.Result && oData.Result['Counts']) { - var aAcounts = AccountStore.collection(); + var + sEmail = Data.accountEmail(), + aAcounts = AccountStore.accounts() + ; _.each(oData.Result['Counts'], function (oItem) { var oAccount = _.find(aAcounts, function (oAccount) { - return oAccount && oItem[0] === oAccount.email; + return oAccount && oItem[0] === oAccount.email && sEmail !== oAccount.email; }); if (oAccount) @@ -518,12 +526,12 @@ { var self = this; - AccountStore.loading(true); + AccountStore.accounts.loading(true); IdentityStore.identities.loading(true); Remote.accountsAndIdentities(function (sResult, oData) { - AccountStore.loading(false); + AccountStore.accounts.loading(false); IdentityStore.identities.loading(false); if (Enums.StorageResultType.Success === sResult && oData.Result) @@ -538,20 +546,26 @@ if (Utils.isArray(oData.Result['Accounts'])) { - _.each(AccountStore.collection(), function (oAccount) { + _.each(AccountStore.accounts(), function (oAccount) { aCounts[oAccount.email] = oAccount.count(); }); - Utils.delegateRunOnDestroy(AccountStore.collection()); + Utils.delegateRunOnDestroy(AccountStore.accounts()); - AccountStore.collection(_.map(oData.Result['Accounts'], function (sValue) { + AccountStore.accounts(_.map(oData.Result['Accounts'], function (sValue) { return new AccountModel(sValue, sValue !== sParentEmail, aCounts[sValue] || 0); })); } if (Utils.isUnd(bBoot) ? false : !!bBoot) { - self.accountsCounts(); + _.delay(function () { + self.accountsCounts(); + }, 1000 * 5); + + Events.sub('interval.10m-after5m', function () { + self.accountsCounts(); + }); } if (Utils.isArray(oData.Result['Identities'])) diff --git a/dev/Common/Selector.js b/dev/Common/Selector.js index 839741fa7..9a9b53139 100644 --- a/dev/Common/Selector.js +++ b/dev/Common/Selector.js @@ -48,7 +48,10 @@ { if (null === this.selectedItem()) { - this.selectedItem.valueHasMutated(); + if (this.selectedItem.valueHasMutated) + { + this.selectedItem.valueHasMutated(); + } } else { diff --git a/dev/External/ko.js b/dev/External/ko.js index 7b09f98ac..ae142fbf2 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -828,7 +828,7 @@ oTarget(''); } } - }) + }).extend({'notify': 'always'}) ; oResult(oTarget()); diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js index 71092e36d..36768f590 100644 --- a/dev/Model/Folder.js +++ b/dev/Model/Folder.js @@ -127,7 +127,7 @@ } }, 'owner': this - }); + }).extend({'notify': 'always'}); this.messageCountUnread = ko.computed({ 'read': this.privateMessageCountUnread, @@ -142,7 +142,7 @@ } }, 'owner': this - }); + }).extend({'notify': 'always'}); this.printableUnreadCount = ko.computed(function () { var diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 0f3c55720..28a3ad9e0 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -12,6 +12,7 @@ Events = require('Common/Events'), Translator = require('Common/Translator'), + AccountStore = require('Stores/User/Account'), SettingsStore = require('Stores/User/Settings'), Data = require('Storage/User/Data'), @@ -111,8 +112,18 @@ SettingsStore.layout.valueHasMutated(); }, 50); - Events.sub('mailbox.inbox-unread-count', function (nCount) { - Data.foldersInboxUnreadCount(nCount); + Events.sub('mailbox.inbox-unread-count', function (iCount) { + + Data.foldersInboxUnreadCount(iCount); + + var sEmail = Data.accountEmail(); + + _.each(AccountStore.accounts(), function (oItem) { + if (oItem && sEmail === oItem.email) + { + oItem.count(iCount); + } + }); }); Data.foldersInboxUnreadCount.subscribe(function () { diff --git a/dev/Settings/Admin/Contacts.js b/dev/Settings/Admin/Contacts.js index d67cd0941..9b82d14e6 100644 --- a/dev/Settings/Admin/Contacts.js +++ b/dev/Settings/Admin/Contacts.js @@ -96,7 +96,7 @@ this.contactsType.valueHasMutated(); } } - }); + }).extend({'notify': 'always'}); this.contactsType.subscribe(function () { this.testContactsSuccess(false); diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index b20a79e4d..bcaba3c4a 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -22,10 +22,10 @@ */ function AccountsUserSettings() { - this.accounts = AccountStore.collection; + this.accounts = AccountStore.accounts; this.processText = ko.computed(function () { - return AccountStore.loading() ? Translator.i18n('SETTINGS_ACCOUNTS/LOADING_PROCESS') : ''; + return AccountStore.accounts.loading() ? Translator.i18n('SETTINGS_ACCOUNTS/LOADING_PROCESS') : ''; }, this); this.visibility = ko.computed(function () { diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js index ed75f0a5c..8eb8a66f5 100644 --- a/dev/Storage/User/Remote.js +++ b/dev/Storage/User/Remote.js @@ -238,8 +238,7 @@ */ RemoteUserStorage.prototype.accountsCounts = function (fCallback) { - return !!fCallback; // TODO -// this.defaultRequest(fCallback, 'AccountsCounts'); + this.defaultRequest(fCallback, 'AccountsCounts'); }; /** diff --git a/dev/Stores/User/Account.js b/dev/Stores/User/Account.js index ebe1f87ff..4eb62f1ba 100644 --- a/dev/Stores/User/Account.js +++ b/dev/Stores/User/Account.js @@ -15,28 +15,43 @@ */ function AccountUserStore() { - this.loading = ko.observable(false).extend({'throttle': 100}); + this.accounts = ko.observableArray([]); + this.accounts.loading = ko.observable(false).extend({'throttle': 100}); - this.collection = ko.observableArray([]); - this.collectionEmailNames = ko.observableArray([]).extend({'throttle': 1000}); - this.collectionEmailNames.skipFirst = true; + this.accountsEmailNames = ko.observableArray([]).extend({'throttle': 1000}); + this.accountsEmailNames.skipFirst = true; - this.collection.subscribe(function (aList) { - this.collectionEmailNames(_.compact(_.map(aList, function (oItem) { + this.accounts.subscribe(function (aList) { + this.accountsEmailNames(_.compact(_.map(aList, function (oItem) { return oItem ? oItem.email : null; }))); }, this); - this.collectionEmailNames.subscribe(function (aList) { - if (this.collectionEmailNames.skipFirst) + this.accountsEmailNames.subscribe(function (aList) { + if (this.accountsEmailNames.skipFirst) { - this.collectionEmailNames.skipFirst = false; + this.accountsEmailNames.skipFirst = false; } else if (aList && 1 < aList.length) { Remote.accountSortOrder(null, aList); } }, this); + + this.accountsUnreadCount = ko.computed(function () { + + var iResult = 0; + + _.each(this.accounts(), function (oItem) { + if (oItem) + { + iResult += oItem.count(); + } + }); + + return iResult; + + }, this); } module.exports = new AccountUserStore(); diff --git a/dev/Stores/User/Notification.js b/dev/Stores/User/Notification.js index 2f5da49d6..b16f1dbe7 100644 --- a/dev/Stores/User/Notification.js +++ b/dev/Stores/User/Notification.js @@ -59,7 +59,7 @@ return iResult; - }, this); + }, this).extend({'notify': 'always'}); this.enableDesktopNotification = ko.computed({ 'owner': this, @@ -117,7 +117,7 @@ this.allowDesktopNotification(false); } } - }); + }).extend({'notify': 'always'}); if (!this.enableDesktopNotification.valueHasMutated) { @@ -150,7 +150,8 @@ this.soundNotificationIsSupported(true); this.buzz = new buzz.sound(Links.sound('new-mail'), { - formats: ['ogg', 'mp3'] + 'preload': 'none', + 'formats': ['mp3', 'ogg'] }); } else diff --git a/dev/Styles/SystemDropDown.less b/dev/Styles/SystemDropDown.less index cc5840c6a..d683f8043 100644 --- a/dev/Styles/SystemDropDown.less +++ b/dev/Styles/SystemDropDown.less @@ -41,9 +41,6 @@ color: #fff; text-shadow: 0 1px 0 #000; - position: absolute; - top: 11px; - right: 70px; display: inline-block; height: 28px; max-width: 250px; @@ -55,6 +52,7 @@ text-overflow: ellipsis; border-radius: 4px; font-weight: bold; + margin-right: 5px; white-space: nowrap; } diff --git a/dev/View/User/AbstractSystemDropDown.js b/dev/View/User/AbstractSystemDropDown.js index 92893e5ac..6a91c0f3d 100644 --- a/dev/View/User/AbstractSystemDropDown.js +++ b/dev/View/User/AbstractSystemDropDown.js @@ -29,18 +29,14 @@ { AbstractView.call(this, 'Right', 'SystemDropDown'); - this.accounts = AccountStore.collection; this.accountEmail = Data.accountEmail; - this.accountsLoading = AccountStore.loading; + + this.accounts = AccountStore.accounts; + this.accountsUnreadCount = AccountStore.accountsUnreadCount; this.accountMenuDropdownTrigger = ko.observable(false); - this.capaAdditionalAccounts = ko.observable(Settings.capa(Enums.Capa.AdditionalAccounts)); - this.loading = ko.computed(function () { - return this.accountsLoading(); - }, this); - this.accountClick = _.bind(this.accountClick, this); } @@ -50,10 +46,10 @@ { if (oAccount && oEvent && !Utils.isUnd(oEvent.which) && 1 === oEvent.which) { - var self = this; - this.accountsLoading(true); + AccountStore.accounts.loading(true); + _.delay(function () { - self.accountsLoading(false); + AccountStore.accounts.loading(false); }, 1000); } diff --git a/package.json b/package.json index 082dc9ed8..39ee22038 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.7.3", - "release": "241", + "release": "243", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php index 24e01d406..40d74653d 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/Http.php @@ -337,11 +337,11 @@ class Http } /** - * @param bool $bCheckProxy = true + * @param bool $bCheckProxy = false * * @return string */ - public function GetClientIp($bCheckProxy = true) + public function GetClientIp($bCheckProxy = false) { $sIp = ''; if ($bCheckProxy && null !== $this->GetServer('HTTP_CLIENT_IP', null)) diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php b/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php index 992ecc82b..47cd599d1 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Log/Logger.php @@ -44,8 +44,10 @@ class Logger extends \MailSo\Base\Collection /** * @access protected + * + * @param bool $bRegPhpErrorHandler = false */ - protected function __construct() + protected function __construct($bRegPhpErrorHandler = true) { parent::__construct(); @@ -55,16 +57,22 @@ class Logger extends \MailSo\Base\Collection $this->bShowSecter = false; $this->bHideErrorNotices = false; - \set_error_handler(array(&$this, '__phpErrorHandler')); + if ($bRegPhpErrorHandler) + { + \set_error_handler(array(&$this, '__phpErrorHandler')); + } + \register_shutdown_function(array(&$this, '__loggerShutDown')); } /** + * @param bool $bRegPhpErrorHandler = false + * * @return \MailSo\Log\Logger */ - public static function NewInstance() + public static function NewInstance($bRegPhpErrorHandler = false) { - return new self(); + return new self($bRegPhpErrorHandler); } /** diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 99bc3dc7c..24a25d95e 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -869,6 +869,24 @@ class MailClient return self::GenerateHash($sFolderName, $iCount, $iUnseenCount, $sUidNext); } + /** + * @return int + * + * @throws \MailSo\Net\Exceptions\Exception + * @throws \MailSo\Imap\Exceptions\Exception + */ + public function InboxUnreadCount() + { + $aFolderStatus = $this->oImapClient->FolderStatus('INBOX', array( + \MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN + )); + + $iResult = isset($aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN]) ? + (int) $aFolderStatus[\MailSo\Imap\Enumerations\FolderResponseStatus::UNSEEN] : 0; + + return 0 < $iResult ? $iResult : 0; + } + /** * @param string $sSearch * @param bool $bDetectGmail = true diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index ea0688180..59f889549 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -40,6 +40,11 @@ class Actions */ private $oLogger; + /** + * @var \MailSo\Log\Logger + */ + private $oLoggerAuth; + /** * @var \RainLoop\Social */ @@ -359,60 +364,119 @@ class Actions } /** + * @param string $sLine + * @param \RainLoop\Model\Account $oAccount = null + * * @return string */ - private function compileLogFileName() + private function compileLogParams($sLine, $oAccount = null) { - $sFileName = (string) $this->Config()->Get('logs', 'filename', ''); - - if (false !== \strpos($sFileName, '{date:')) + if (false !== \strpos($sLine, '{date:')) { - $sFileName = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) { + $sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) { return \gmdate($aMatch[1]); - }, $sFileName); + }, $sLine); - $sFileName = \preg_replace('/\{date:([^}]*)\}/', 'date', $sFileName); + $sLine = \preg_replace('/\{date:([^}]*)\}/', 'date', $sLine); } - if (false !== \strpos($sFileName, '{user:')) + if (false !== \strpos($sLine, '{imap:') || false !== \strpos($sLine, '{smtp:')) { - if (false !== \strpos($sFileName, '{user:uid}')) + if (!$oAccount) { - $sFileName = \str_replace('{user:uid}', + $this->ParseQueryAuthString(); + $oAccount = $this->getAccountFromToken(false); + } + + if ($oAccount) + { + $sLine = \str_replace('{imap:login}', $oAccount->IncLogin(), $sLine); + $sLine = \str_replace('{imap:host}', $oAccount->DomainIncHost(), $sLine); + $sLine = \str_replace('{imap:port}', $oAccount->DomainIncPort(), $sLine); + + $sLine = \str_replace('{smtp:login}', $oAccount->OutLogin(), $sLine); + $sLine = \str_replace('{smtp:host}', $oAccount->DomainOutHost(), $sLine); + $sLine = \str_replace('{smtp:port}', $oAccount->DomainOutPort(), $sLine); + } + + $sLine = \preg_replace('/\{imap:([^}]*)\}/i', 'imap', $sLine); + $sLine = \preg_replace('/\{smtp:([^}]*)\}/i', 'imap', $sLine); + } + + if (false !== \strpos($sLine, '{request:')) + { + if (false !== \strpos($sLine, '{request:ip}')) + { + $sLine = \str_replace('{request:ip}', $this->Http()->GetClientIp( + $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine); + } + + $sLine = \preg_replace('/\{request:([^}]*)\}/i', 'request', $sLine); + } + + if (false !== \strpos($sLine, '{user:')) + { + if (false !== \strpos($sLine, '{user:uid}')) + { + $sLine = \str_replace('{user:uid}', \base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), - $sFileName + $sLine ); } - if (false !== \strpos($sFileName, '{user:ip}')) + if (false !== \strpos($sLine, '{user:ip}')) { - $sFileName = \str_replace('{user:ip}', $this->Http()->GetClientIp(), $sFileName); + $sLine = \str_replace('{user:ip}', $this->Http()->GetClientIp( + $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine); } - if (\preg_match('/\{user:(email|login|domain)\}/i', $sFileName)) + if (\preg_match('/\{user:(email|login|domain)\}/i', $sLine)) { - $this->ParseQueryAuthString(); + if (!$oAccount) + { + $this->ParseQueryAuthString(); + $oAccount = $this->getAccountFromToken(false); + } - $oAccount = $this->getAccountFromToken(false); if ($oAccount) { $sEmail = $oAccount->Email(); - $sFileName = \str_replace('{user:email}', $sEmail, $sFileName); - $sFileName = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sFileName); - $sFileName = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sFileName); + $sLine = \str_replace('{user:email}', $sEmail, $sLine); + $sLine = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine); + $sLine = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine); } } - $sFileName = \preg_replace('/\{user:([^}]*)\}/i', 'unknown', $sFileName); + $sLine = \preg_replace('/\{user:([^}]*)\}/i', 'unknown', $sLine); } - if (false !== \strpos($sFileName, '{labs:')) + if (false !== \strpos($sLine, '{labs:')) { - $sFileName = \preg_replace_callback('/\{labs:rand:([1-9])\}/', function ($aMatch) { + $sLine = \preg_replace_callback('/\{labs:rand:([1-9])\}/', function ($aMatch) { return \rand(\pow(10, $aMatch[1] - 1), \pow(10, $aMatch[1]) - 1); - }, $sFileName); + }, $sLine); - $sFileName = \preg_replace('/\{labs:([^}]*)\}/', 'labs', $sFileName); + $sLine = \preg_replace('/\{labs:([^}]*)\}/', 'labs', $sLine); + } + + return $sLine; + } + + /** + * @param string $sFileName + * + * @return string + */ + private function compileLogFileName($sFileName) + { + $sFileName = \trim($sFileName); + + if (0 !== \strlen($sFileName)) + { + $sFileName = $this->compileLogParams($sFileName); + + $sFileName = \preg_replace('/[\/]+/', '/', \preg_replace('/[.]+/', '.', $sFileName)); + $sFileName = \preg_replace('/[^a-zA-Z0-9@_+=\-\.\/!()\[\]]/', '', $sFileName); } if (0 === \strlen($sFileName)) @@ -420,9 +484,6 @@ class Actions $sFileName = 'rainloop-log.txt'; } - $sFileName = \preg_replace('/[\/]+/', '/', \preg_replace('/[.]+/', '.', $sFileName)); - $sFileName = \preg_replace('/[^a-zA-Z0-9@_+=\-\.\/!()\[\]]/', '', $sFileName); - return $sFileName; } @@ -775,11 +836,13 @@ class Actions { $this->oLogger = \MailSo\Log\Logger::SingletonInstance(); - if (!!$this->Config()->Get('logs', 'enable', true)) + if (!!$this->Config()->Get('logs', 'enable', false)) { $this->oLogger->SetShowSecter(!$this->Config()->Get('logs', 'hide_passwords', true)); - $sLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName(); + $sLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName( + $this->Config()->Get('logs', 'filename', '')); + $sLogFileDir = \dirname($sLogFileFullPath); if (!@is_dir($sLogFileDir)) @@ -804,7 +867,8 @@ class Actions $oHttp = $this->Http(); $this->oLogger->Write('[DATE:'.\gmdate('d.m.y').'][RL:'.APP_VERSION.'][PHP:'.PHP_VERSION.'][IP:'. - $oHttp->GetClientIp().'][PID:'.(\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? \getmypid() : 'unknown').']['. + $oHttp->GetClientIp($this->Config()->Get('labs', 'http_client_ip_check_proxy', false)).'][PID:'. + (\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? \getmypid() : 'unknown').']['. $oHttp->GetServer('SERVER_SOFTWARE', '~').']['. (\MailSo\Base\Utils::FunctionExistsAndEnabled('php_sapi_name') ? \php_sapi_name() : '~' ).']' ); @@ -829,6 +893,40 @@ class Actions return $this->oLogger; } + /** + * @return \MailSo\Log\Logger + */ + public function LoggerAuth() + { + if (null === $this->oLoggerAuth) + { + $this->oLoggerAuth = \MailSo\Log\Logger::NewInstance(false); + + if (!!$this->Config()->Get('logs', 'auth_logging', false)) + { + $sAuthLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName( + $this->Config()->Get('logs', 'auth_logging_filename', '')); + + $sLogFileDir = \dirname($sAuthLogFileFullPath); + + if (!@is_dir($sLogFileDir)) + { + @mkdir($sLogFileDir, 0755, true); + } + + $this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::MEMORY); + $this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME); + $this->oLoggerAuth->AddForbiddenType(\MailSo\Log\Enumerations\Type::TIME_DELTA); + + $this->oLoggerAuth->Add( + \MailSo\Log\Drivers\File::NewInstance($sAuthLogFileFullPath) + ); + } + } + + return $this->oLoggerAuth; + } + /** * @return array */ @@ -1524,10 +1622,11 @@ class Actions /** * @param \RainLoop\Model\Account $oAccount + * @param bool $bAuthLog = false * * @throws \RainLoop\Exceptions\ClientException */ - public function CheckMailConnection($oAccount) + public function CheckMailConnection($oAccount, $bAuthLog = false) { try { @@ -1543,6 +1642,16 @@ class Actions } catch (\MailSo\Imap\Exceptions\LoginBadCredentialsException $oException) { + if ($bAuthLog) + { + $sLine = $this->Config()->Get('logs', 'auth_logging_format', ''); + if (!empty($sLine)) + { + $this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount), + \MailSo\Log\Enumerations\Type::WARNING, 'IMAP'); + } + } + if ($this->Config()->Get('labs', 'imap_show_login_alert', true)) { throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError, @@ -1724,7 +1833,7 @@ class Actions try { - $this->CheckMailConnection($oAccount); + $this->CheckMailConnection($oAccount, true); } catch (\Exception $oException) { @@ -2373,6 +2482,40 @@ class Actions )); } + /** + * @param string $sHash + * + * @return int + * + * @throws \MailSo\Base\Exceptions\Exception + */ + public function getAccountUnredCountFromHash($sHash) + { + $iResult = 0; + + $oAccount = $this->GetAccountFromCustomToken($sHash, false); + if ($oAccount) + { + try + { + $oMailClient = \MailSo\Mail\MailClient::NewInstance(); + $oMailClient->SetLogger($this->Logger()); + + $oAccount->IncConnectAndLoginHelper($this->Plugins(),$oMailClient, $this->Config()); + + $iResult = $oMailClient->InboxUnreadCount(); + + $oMailClient->LogoutAndDisconnect(); + } + catch (\Exception $oException) + { + $this->Logger()->WriteException($oException); + } + } + + return $iResult; + } + /** * @return array * @@ -2382,13 +2525,32 @@ class Actions { $oAccount = $this->getAccountFromToken(); + $bComplete = true; $aCounts = array(); + if ($this->Config()->Get('webmail', 'allow_additional_accounts', true)) { + $iLimit = 7; $mAccounts = $this->GetAccounts($oAccount); - foreach ($mAccounts as $sEmail => $sHash) + if (\is_array($mAccounts) && 0 < \count($mAccounts)) { - $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail), 0); + if ($iLimit > \count($mAccounts)) + { + $mAccounts = \array_slice($mAccounts, 0, $iLimit); + } + else + { + $bComplete = false; + } + + if (0 < \count($mAccounts)) + { + foreach ($mAccounts as $sEmail => $sHash) + { + $aCounts[] = array(\MailSo\Base\Utils::IdnToUtf8($sEmail), + $oAccount->Email() === $sEmail ? 0 : $this->getAccountUnredCountFromHash($sHash)); + } + } } } else @@ -2397,7 +2559,7 @@ class Actions } return $this->DefaultResponse(__FUNCTION__, array( - 'Complete' => true, + 'Complete' => $bComplete, 'Counts' => $aCounts )); } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php index c948259cb..46f0595ce 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -188,12 +188,22 @@ Patterns: {user:domain} - Replaced by user\'s domain name (the domain part of an email) If user is not logged in, value is set to "unknown" {user:uid} - Replaced by user\'s UID regardless of account currently used - {user:ip} - Replaced by user\'s IP address + + {user:ip} + {request:ip} - Replaced by user\'s IP address + +Others: + {imap:login} {imap:host} {imap:port} + {smtp:login} {smtp:host} {smtp:port} Examples: filename = "log-{date:Y-m-d}.txt" filename = "{date:Y-m-d}/{user:domain}/{user:email}_{user:uid}.log" - filename = "{user:email}-{date:Y-m-d}.txt"') + filename = "{user:email}-{date:Y-m-d}.txt"'), + + 'auth_logging' => array(false, 'Enable auth logging in a separate file (for fail2ban)'), + 'auth_logging_filename' => array('fail2ban/auth-{date:Y-m-d}.txt'), + 'auth_logging_format' => array('Auth failed: ip={request:ip} user={imap:login} host={imap:host} port={imap:port}') ), 'debug' => array( @@ -287,6 +297,7 @@ Enables caching in the system'), 'allow_external_login' => array(false), 'allow_external_sso' => array(false), 'external_sso_key' => array(''), + 'http_client_ip_check_proxy' => array(false), 'fast_cache_memcache_host' => array('127.0.0.1'), 'fast_cache_memcache_port' => array(11211), 'fast_cache_memcache_expire' => array(43200), diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html index be1cb9d22..a2f30328c 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SystemDropDown.html @@ -1,11 +1,15 @@