From d76ba3abc9bfec65515562c0afbb3e27777550f4 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 10 Nov 2022 19:20:09 +0100 Subject: [PATCH 001/114] Fix Config handling --- .../libraries/RainLoop/Config/AbstractConfig.php | 11 +---------- .../app/libraries/RainLoop/Config/Plugin.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php index aa0ef07aa..f0d6bf970 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/AbstractConfig.php @@ -58,16 +58,7 @@ abstract class AbstractConfig implements \JsonSerializable #[\ReturnTypeWillChange] public function jsonSerialize() { - $aData = []; - foreach ($this->aData as $sSectionKey => $aSectionValue) { - if (\is_array($aSectionValue)) { - $aData[$sSectionKey] = []; - foreach ($aSectionValue as $sParamKey => $mParamValue) { - $aData[$sSectionKey][$sParamKey] = $mParamValue[0]; - } - } - } - return $aData; + return $this->aData;; } /** diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Plugin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Plugin.php index 9c68aa76c..a032029ee 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Plugin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Plugin.php @@ -37,4 +37,19 @@ class Plugin extends \RainLoop\Config\AbstractConfig { return $this->aMap; } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $aData = []; + foreach ($this->aData as $sSectionKey => $aSectionValue) { + if (\is_array($aSectionValue)) { + $aData[$sSectionKey] = []; + foreach ($aSectionValue as $sParamKey => $mParamValue) { + $aData[$sSectionKey][$sParamKey] = $mParamValue[0]; + } + } + } + return $aData; + } } From cc1d0a6e38dfd62785ee84c7f082f63fe67616dc Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 11 Nov 2022 13:45:40 +0100 Subject: [PATCH 002/114] Allow setting SSL options per domain and verify_certificate by default This is a complete overhaul of the Domain settings system. --- dev/View/Popup/Domain.js | 122 ++++-- snappymail/v/0.0.0/app/domains/default.json | 62 ++- .../app/libraries/MailSo/Imap/ImapClient.php | 18 +- .../app/libraries/MailSo/Imap/Settings.php | 34 ++ .../libraries/MailSo/Net/ConnectSettings.php | 88 ++-- .../app/libraries/MailSo/Net/NetClient.php | 10 +- .../app/libraries/MailSo/Net/SSLContext.php | 73 ++++ .../MailSo/Sieve/ManageSieveClient.php | 14 +- .../app/libraries/MailSo/Sieve/Settings.php | 52 +++ .../app/libraries/MailSo/Smtp/Settings.php | 51 +++ .../app/libraries/MailSo/Smtp/SmtpClient.php | 14 +- .../app/libraries/RainLoop/ActionsAdmin.php | 46 +- .../v/0.0.0/app/libraries/RainLoop/Api.php | 21 - .../libraries/RainLoop/Config/Application.php | 13 +- .../app/libraries/RainLoop/Model/Account.php | 107 ++--- .../app/libraries/RainLoop/Model/Domain.php | 400 ++++++------------ .../libraries/RainLoop/Providers/Domain.php | 20 +- .../Providers/Domain/DefaultDomain.php | 1 - .../v/0.0.0/app/localization/cs-CZ/admin.json | 4 +- .../v/0.0.0/app/localization/da-DK/admin.json | 4 +- .../v/0.0.0/app/localization/de-DE/admin.json | 4 +- .../v/0.0.0/app/localization/en/admin.json | 4 +- .../v/0.0.0/app/localization/es-ES/admin.json | 4 +- .../v/0.0.0/app/localization/fa-IR/admin.json | 4 +- .../v/0.0.0/app/localization/fi-FI/admin.json | 4 +- .../v/0.0.0/app/localization/fr-FR/admin.json | 4 +- .../v/0.0.0/app/localization/hu-HU/admin.json | 4 +- .../v/0.0.0/app/localization/id-ID/admin.json | 4 +- .../v/0.0.0/app/localization/it-IT/admin.json | 4 +- .../v/0.0.0/app/localization/ja-JP/admin.json | 4 +- .../v/0.0.0/app/localization/lt-LT/admin.json | 4 +- .../v/0.0.0/app/localization/nb-NO/admin.json | 4 +- .../v/0.0.0/app/localization/nl-NL/admin.json | 4 +- .../v/0.0.0/app/localization/pl-PL/admin.json | 4 +- .../v/0.0.0/app/localization/pt-BR/admin.json | 4 +- .../v/0.0.0/app/localization/pt-PT/admin.json | 4 +- .../v/0.0.0/app/localization/pt/admin.json | 4 +- .../v/0.0.0/app/localization/ru-RU/admin.json | 4 +- .../v/0.0.0/app/localization/sk-SK/admin.json | 4 +- .../v/0.0.0/app/localization/sl-SI/admin.json | 4 +- .../v/0.0.0/app/localization/sv-SE/admin.json | 4 +- .../v/0.0.0/app/localization/vi-VN/admin.json | 4 +- .../v/0.0.0/app/localization/zh-CN/admin.json | 4 +- .../Views/Admin/AdminSettingsSecurity.html | 3 +- .../templates/Views/Admin/PopupsDomain.html | 84 +++- 45 files changed, 688 insertions(+), 645 deletions(-) create mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Imap/Settings.php create mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Net/SSLContext.php create mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Sieve/Settings.php create mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Smtp/Settings.php diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index a2022a725..11432b1ce 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -12,24 +12,49 @@ import { DomainAdminStore } from 'Stores/Admin/Domain'; import { AskPopupView } from 'View/Popup/Ask'; -const domainToParams = oDomain => ({ - Name: oDomain.name(), - - IncHost: oDomain.imapHost(), - IncPort: oDomain.imapPort(), - IncSecure: oDomain.imapSecure(), - - UseSieve: oDomain.useSieve() ? 1 : 0, - SieveHost: oDomain.sieveHost(), - SievePort: oDomain.sievePort(), - SieveSecure: oDomain.sieveSecure(), - - OutHost: oDomain.smtpHost(), - OutPort: oDomain.smtpPort(), - OutSecure: oDomain.smtpSecure(), - OutAuth: oDomain.smtpAuth() ? 1 : 0, - OutUsePhpMail: oDomain.smtpPhpMail() ? 1 : 0 - }); +const + capitalize = string => string.charAt(0).toUpperCase() + string.slice(1), + domainToParams = oDomain => ({ + Name: oDomain.name(), + IMAP: { + host: oDomain.imapHost(), + port: oDomain.imapPort(), + secure: pInt(oDomain.imapType()), + shortLogin: !!oDomain.imapShortLogin(), + ssl: { + verify_peer: !!oDomain.imapSslVerify_peer(), + verify_peer_name: !!oDomain.imapSslVerify_peer(), + allow_self_signed: !!oDomain.imapSslAllow_self_signed() + } + }, + SMTP: { + host: oDomain.smtpHost(), + port: oDomain.smtpPort(), + secure: pInt(oDomain.smtpType()), + shortLogin: !!oDomain.smtpShortLogin(), + ssl: { + verify_peer: !!oDomain.smtpSslVerify_peer(), + verify_peer_name: !!oDomain.smtpSslVerify_peer(), + allow_self_signed: !!oDomain.smtpSslAllow_self_signed() + }, + setSender: !!oDomain.smtpSetSender(), + useAuth: !!oDomain.smtpUseAuth(), + usePhpMail: !!oDomain.smtpUsePhpMail() + }, + Sieve: { + enabled: !!oDomain.sieveEnabled(), + host: oDomain.sieveHost(), + port: oDomain.sievePort(), + secure: pInt(oDomain.sieveType()), + shortLogin: !!oDomain.imapShortLogin(), + ssl: { + verify_peer: !!oDomain.imapSslVerify_peer(), + verify_peer_name: !!oDomain.imapSslVerify_peer(), + allow_self_signed: !!oDomain.imapSslAllow_self_signed() + } + }, + whiteList: oDomain.whiteList() + }); export class DomainPopupView extends AbstractViewPopup { constructor() { @@ -82,14 +107,14 @@ export class DomainPopupView extends AbstractViewPopup { }, domainIsComputed: () => { - const usePhpMail = this.smtpPhpMail(), - useSieve = this.useSieve(); + const usePhpMail = this.smtpUsePhpMail(), + sieveEnabled = this.sieveEnabled(); return ( this.name() && this.imapHost() && this.imapPort() && - (useSieve ? this.sieveHost() && this.sievePort() : true) && + (sieveEnabled ? this.sieveHost() && this.sievePort() : true) && ((this.smtpHost() && this.smtpPort()) || usePhpMail) ); }, @@ -113,7 +138,7 @@ export class DomainPopupView extends AbstractViewPopup { smtpHostFocus: value => value && this.imapHost() && !this.smtpHost() && this.smtpHost(this.imapHost().replace(/imap/gi, 'smtp')), - imapSecure: value => { + imapType: value => { if (this.enableSmartPorts()) { const port = pInt(this.imapPort()); switch (pInt(value)) { @@ -133,7 +158,7 @@ export class DomainPopupView extends AbstractViewPopup { } }, - smtpSecure: value => { + smtpType: value => { if (this.enableSmartPorts()) { const port = pInt(this.smtpPort()); switch (pInt(value)) { @@ -169,14 +194,7 @@ export class DomainPopupView extends AbstractViewPopup { Remote.request('AdminDomainSave', this.onDomainCreateOrSaveResponse.bind(this), Object.assign(domainToParams(this), { - Create: this.edit() ? 0 : 1, - - IncShortLogin: this.imapShortLogin() ? 1 : 0, - - OutShortLogin: this.smtpShortLogin() ? 1 : 0, - OutSetSender: this.smtpSetSender() ? 1 : 0, - - WhiteList: this.whiteList() + Create: this.edit() ? 0 : 1 }) ); } @@ -188,8 +206,10 @@ export class DomainPopupView extends AbstractViewPopup { if (credentials) { this.testing(true); const params = domainToParams(this); - params.username = credentials.username; - params.password = credentials.password; + params.auth = { + user: credentials.username, + pass: credentials.password + }; Remote.request('AdminDomainTest', (iError, oData) => { this.testing(false); @@ -250,7 +270,23 @@ export class DomainPopupView extends AbstractViewPopup { if (oDomain) { this.enableSmartPorts(false); this.edit(true); - forEachObjectEntry(oDomain, (key, value) => this[key]?.(value)); + forEachObjectEntry(oDomain, (key, value) => { + if ('IMAP' === key || 'SMTP' === key || 'Sieve' === key) { + key = key.toLowerCase(); + forEachObjectEntry(value, (skey, value) => { + skey = capitalize(skey); + if ('Ssl' == skey) { + forEachObjectEntry(value, (sslkey, value) => { + this[key + skey + capitalize(sslkey)]?.(value); + }); + } else { + this[key + skey]?.(value); + } + }); + } else { + this[key]?.(value); + } + }); this.enableSmartPorts(true); } } @@ -265,21 +301,27 @@ export class DomainPopupView extends AbstractViewPopup { imapHost: '', imapPort: 143, - imapSecure: 0, + imapType: 0, imapShortLogin: false, + // SSL + imapSslVerify_peer: false, + imapSslAllow_self_signed: false, - useSieve: false, + sieveEnabled: false, sieveHost: '', sievePort: 4190, - sieveSecure: 0, + sieveType: 0, smtpHost: '', smtpPort: 25, - smtpSecure: 0, + smtpType: 0, smtpShortLogin: false, - smtpAuth: true, + smtpUseAuth: true, smtpSetSender: false, - smtpPhpMail: false, + smtpUsePhpMail: false, + // SSL + smtpSslVerify_peer: false, + smtpSslAllow_self_signed: false, whiteList: '', aliasName: '' diff --git a/snappymail/v/0.0.0/app/domains/default.json b/snappymail/v/0.0.0/app/domains/default.json index 14ab64608..18f8e8102 100644 --- a/snappymail/v/0.0.0/app/domains/default.json +++ b/snappymail/v/0.0.0/app/domains/default.json @@ -1,18 +1,50 @@ { - "imapHost": "localhost", - "imapPort": 143, - "imapSecure": 0, - "imapShortLogin": false, - "useSieve": false, - "sieveHost": "localhost", - "sievePort": 4190, - "sieveSecure": 0, - "smtpHost": "localhost", - "smtpPort": 25, - "smtpSecure": 0, - "smtpShortLogin": false, - "smtpAuth": true, - "smtpSetSender": false, - "smtpPhpMail": false, + "name": "*", + "IMAP": { + "host": "localhost", + "port": 143, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + } + }, + "SMTP": { + "host": "localhost", + "port": 25, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + }, + "useAuth": false, + "setSender": false, + "usePhpMail": false + }, + "Sieve": { + "host": "localhost", + "port": 4190, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + }, + "enabled": true + }, "whiteList": "" } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php index 615cf2c79..105c94ab3 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/ImapClient.php @@ -120,15 +120,15 @@ class ImapClient extends \MailSo\Net\NetClient * @throws \MailSo\Net\Exceptions\* * @throws \MailSo\Imap\Exceptions\* */ - public function Login(array $aCredentials) : self + public function Login(Settings $oSettings) : self { - if (!empty($aCredentials['ProxyAuthUser']) && !empty($aCredentials['ProxyAuthPassword'])) { - $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($aCredentials['ProxyAuthUser'])); - $sPassword = $aCredentials['ProxyAuthPassword']; - $sProxyAuthUser = $aCredentials['Login']; + if (!empty($oSettings->ProxyAuthUser) && !empty($oSettings->ProxyAuthPassword)) { + $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($oSettings->ProxyAuthUser)); + $sPassword = $oSettings->ProxyAuthPassword; + $sProxyAuthUser = $oSettings->Login; } else { - $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($aCredentials['Login'])); - $sPassword = $aCredentials['Password']; + $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($oSettings->Login)); + $sPassword = $oSettings->Password; $sProxyAuthUser = ''; } @@ -142,7 +142,7 @@ class ImapClient extends \MailSo\Net\NetClient $this->sLogginedUser = $sLogin; $type = $this->IsSupported('LOGINDISABLED') ? '' : 'LOGIN'; // RFC3501 6.2.3 - foreach ($aCredentials['SASLMechanisms'] as $sasl_type) { + foreach ($oSettings->SASLMechanisms as $sasl_type) { if ($this->IsSupported("AUTH={$sasl_type}") && \SnappyMail\SASL::isSupported($sasl_type)) { $type = $sasl_type; break; @@ -151,7 +151,7 @@ class ImapClient extends \MailSo\Net\NetClient if (!$type) { if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) { $this->StartTLS(); - return $this->Login($aCredentials); + return $this->Login($oSettings); } throw new \MailSo\RuntimeException('No supported SASL mechanism found, remote server wants: ' . \implode(', ', \array_filter($this->Capability() ?: [], function($var){ diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Settings.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Settings.php new file mode 100644 index 000000000..85bfaa872 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/Settings.php @@ -0,0 +1,34 @@ + 'Object/ImapSettings', + ] + ); + } +*/ +} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php b/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php index 8bb34edbd..548828d3f 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php @@ -15,66 +15,58 @@ namespace MailSo\Net; * @category MailSo * @package Net */ -class ConnectSettings +class ConnectSettings implements \JsonSerializable { - public - $host, + public string $host; - $port, + public int $port; - // none, TLS, STARTTLS - $type = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT, + // none, TLS, STARTTLS + public int $type = Enumerations\ConnectionSecurityType::AUTO_DETECT; - // https://www.php.net/context.ssl - $ssl = [ -// 'peer_name' => '', -// 'peer_fingerprint' => '', // string | array - 'verify_peer' => true, - 'verify_peer_name' => true, - 'allow_self_signed' => false, -// 'cafile' => '', -// 'capath' => '', + public SSLContext $ssl; -// 'ciphers' => 'HIGH:!SSLv2:!SSLv3', - 'SNI_enabled' => true, - 'disable_compression' => true, - 'security_level' => 1, + // Authentication settings use by all child classes + public bool $useAuth = true; + public bool $shortLogin = false; + public array $SASLMechanisms = []; + public string $Login = ''; + public string $Password = ''; + public string $ProxyAuthUser = ''; + public string $ProxyAuthPassword = ''; -// 'local_cert' => '', -// 'local_pk' => '', -// 'passphrase' => '', - -// 'verify_depth' => 0, -// 'capture_peer_cert' => false, -// 'capture_peer_cert_chain' => false, - ]; - - function __construct() + public function __construct() { - // TODO: This should be moved to \RainLoop\Model\Domain - $oConfig = \RainLoop\API::Config(); - $this->ssl['verify_peer'] = !!$oConfig->Get('ssl', 'verify_certificate', false); - $this->ssl['verify_peer_name'] = !!$oConfig->Get('ssl', 'verify_certificate', false); - $this->ssl['allow_self_signed'] = !!$oConfig->Get('ssl', 'allow_self_signed', true); - $this->ssl['security_level'] = (int) $oConfig->Get('ssl', 'security_level', 1); -// $this->ssl['local_cert'] = (string) $oConfig->Get('ssl', 'client_cert', ''); -// $this->ssl['cafile'] = (string) $oConfig->Get('ssl', 'cafile', ''); -// $this->ssl['capath'] = (string) $oConfig->Get('ssl', 'capath', ''); + $this->ssl = new SSLContext; + } + + public static function Host() : string + { + return \SnappyMail\IDN::toAscii($this->host); } public static function fromArray(array $aSettings) : self { - $object = new self; - $object->host = $aSettings['Host']; - $object->port = $aSettings['Port']; - $object->type = $aSettings['Secure']; - $object->ssl['verify_peer'] = !empty($aSettings['VerifySsl']); - $object->ssl['verify_peer_name'] = !empty($aSettings['VerifySsl']); - $object->ssl['allow_self_signed'] = !empty($aSettings['AllowSelfSigned']); - if (!empty($aSettings['ClientCert'])) { - $object->ssl['local_cert'] = $aSettings['ClientCert']; - } + $object = new static; + $object->host = $aSettings['host']; + $object->port = $aSettings['port']; + $object->type = $aSettings['secure']; + $object->shortLogin = !empty($aSettings['shortLogin']); + $object->ssl = SSLContext::fromArray($aSettings['ssl'] ?? []); return $object; } + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array( +// '@Object' => 'Object/ConnectSettings', + 'host' => $this->host, + 'port' => $this->port, + 'secure' => $this->type, + 'shortLogin' => $this->shortLogin, + 'ssl' => $this->ssl + ); + } + } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php index ed0e78ba4..3d28ca305 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Net/NetClient.php @@ -162,13 +162,9 @@ abstract class NetClient $this->iStartConnectTime = \microtime(true); $this->writeLog('Start connection to "'.$this->sConnectedHost.':'.$this->iConnectedPort.'"'); - $aStreamContextSettings = array( - 'ssl' => $oSettings->ssl - ); - - \MailSo\Hooks::Run('Net.NetClient.StreamContextSettings/Filter', array(&$aStreamContextSettings)); - - $rStreamContext = \stream_context_create($aStreamContextSettings); + $rStreamContext = \stream_context_create(array( + 'ssl' => $oSettings->ssl->jsonSerialize() + )); \set_error_handler(array($this, 'capturePhpErrorWithException')); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Net/SSLContext.php b/snappymail/v/0.0.0/app/libraries/MailSo/Net/SSLContext.php new file mode 100644 index 000000000..e34972061 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Net/SSLContext.php @@ -0,0 +1,73 @@ +verify_peer = !!$oConfig->Get('ssl', 'verify_certificate', true); + $this->verify_peer_name = !!$oConfig->Get('ssl', 'verify_certificate', true); + $this->allow_self_signed = !!$oConfig->Get('ssl', 'allow_self_signed', false); + $this->cafile = \trim($oConfig->Get('ssl', 'cafile', '')); + $this->capath = \trim($oConfig->Get('ssl', 'capath', '')); + $this->disable_compression = !!$oConfig->Get('ssl', 'disable_compression', true); + $this->security_level = (int) $oConfig->Get('ssl', 'security_level', 1); + $this->local_cert = \trim($oConfig->Get('ssl', 'local_cert', '')); + } + + public static function fromArray(array $settings) : self + { + $object = new static; + foreach ($settings as $key => $value) { + $object->$key = $value; + } + return $object; + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $aResult = \get_object_vars($this); +// $aResult['@Object'] = 'Object/SSLContext'; + return \array_filter( + $aResult, + function($var){return !\is_string($var) || \strlen($var);} + ); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php index c67ca7c7a..3d4c97653 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/ManageSieveClient.php @@ -100,10 +100,10 @@ class ManageSieveClient extends \MailSo\Net\NetClient * @throws \MailSo\Net\Exceptions\* * @throws \MailSo\Sieve\Exceptions\* */ - public function Login(array $aCredentials) : self + public function Login(Settings $oSettings) : self { - $sLogin = $aCredentials['Login']; - $sPassword = $aCredentials['Password']; + $sLogin = $oSettings->Login; + $sPassword = $oSettings->Password; $sLoginAuthKey = ''; if (!\strlen($sLogin) || !\strlen($sPassword)) { @@ -113,8 +113,8 @@ class ManageSieveClient extends \MailSo\Net\NetClient } $type = ''; - \array_push($aCredentials['SASLMechanisms'], 'PLAIN', 'LOGIN'); - foreach ($aCredentials['SASLMechanisms'] as $sasl_type) { + \array_push($oSettings->SASLMechanisms, 'PLAIN', 'LOGIN'); + foreach ($oSettings->SASLMechanisms as $sasl_type) { if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) { $type = $sasl_type; break; @@ -124,7 +124,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient if (!$type) { if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) { $this->StartTLS(); - return $this->Login($aCredentials); + return $this->Login($oSettings); } $this->writeLogException( new \MailSo\Sieve\Exceptions\LoginException, @@ -154,7 +154,7 @@ class ManageSieveClient extends \MailSo\Net\NetClient $sAuth = $SASL->authenticate($sLogin, $sPassword, $sLoginAuthKey); $this->oLogger && $this->oLogger->AddSecret($sAuth); - if ($aCredentials['InitialAuthPlain']) { + if ($oSettings->initialAuthPlain) { $this->sendRaw("AUTHENTICATE \"{$type}\" \"{$sAuth}\""); } else { $this->sendRaw("AUTHENTICATE \"{$type}\" {".\strlen($sAuth).'+}'); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/Settings.php b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/Settings.php new file mode 100644 index 000000000..02ee69f5e --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Sieve/Settings.php @@ -0,0 +1,52 @@ +initialAuthPlain = !!$oConfig->Get('labs', 'sieve_auth_plain_initial', true); + } + + public static function fromArray(array $aSettings) : self + { + $object = parent::fromArray($aSettings); + $object->enabled = !empty($aSettings['enabled']); + return $object; + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return \array_merge( + parent::jsonSerialize(), + [ +// '@Object' => 'Object/SmtpSettings', + 'enabled' => $this->enabled + ] + ); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/Settings.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/Settings.php new file mode 100644 index 000000000..3ccdac36f --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/Settings.php @@ -0,0 +1,51 @@ +useAuth = !empty($aSettings['useAuth']); + $object->setSender = !empty($aSettings['setSender']); + $object->usePhpMail = !empty($aSettings['usePhpMail']); + return $object; + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return \array_merge( + parent::jsonSerialize(), + [ +// '@Object' => 'Object/SmtpSettings', + 'useAuth' => $this->useAuth, + 'setSender' => $this->setSender, + 'usePhpMail' => $this->usePhpMail + ] + ); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php index 53d162ee6..88d79ebee 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Smtp/SmtpClient.php @@ -137,16 +137,16 @@ class SmtpClient extends \MailSo\Net\NetClient * @throws \MailSo\Net\* * @throws \MailSo\Smtp\Exceptions\* */ - public function Login(array $aCredentials) : self + public function Login(Settings $oSettings) : self { - $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($aCredentials['Login'])); - $sPassword = $aCredentials['Password']; + $sLogin = \MailSo\Base\Utils::IdnToAscii(\MailSo\Base\Utils::Trim($oSettings->Login)); + $sPassword = $oSettings->Password; $type = ''; // https://github.com/the-djmaze/snappymail/pull/423 -// $aCredentials['SASLMechanisms'][] = 'LOGIN'; - \array_unshift($aCredentials['SASLMechanisms'], 'LOGIN'); - foreach ($aCredentials['SASLMechanisms'] as $sasl_type) { +// $oSettings->SASLMechanisms[] = 'LOGIN'; + \array_unshift($oSettings->SASLMechanisms, 'LOGIN'); + foreach ($oSettings->SASLMechanisms as $sasl_type) { if ($this->IsAuthSupported($sasl_type) && \SnappyMail\SASL::isSupported($sasl_type)) { $type = $sasl_type; break; @@ -156,7 +156,7 @@ class SmtpClient extends \MailSo\Net\NetClient if (!$type) { if (!$this->Encrypted() && $this->IsSupported('STARTTLS')) { $this->StartTLS(); - return $this->Login($aCredentials); + return $this->Login($oSettings); } \trigger_error("SMTP {$this->GetConnectedHost()} no supported AUTH options. Disable login"); $this->writeLogException( diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php index d96e738fa..ec5f03082 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php @@ -269,35 +269,21 @@ class ActionsAdmin extends Actions $oDomain = $this->DomainProvider()->LoadOrCreateNewFromAction($this, 'test.example.com'); if ($oDomain) { + $aAuth = $this->GetActionParam('auth'); + try { $oImapClient = new \MailSo\Imap\ImapClient(); $oImapClient->SetLogger($this->Logger()); $oImapClient->SetTimeOuts($iConnectionTimeout); - $iTime = \microtime(true); - $oSettings = \MailSo\Net\ConnectSettings::fromArray($oDomain->ImapSettings()); + $oSettings = $oDomain->ImapSettings(); $oImapClient->Connect($oSettings); - $sUsername = $this->GetActionParam('username', ''); - if ($sUsername) { - $aSASLMechanisms = []; - $oConfig = $this->Config(); - if ($oConfig->Get('labs', 'sasl_allow_scram_sha', false)) { - // https://github.com/the-djmaze/snappymail/issues/182 - \array_push($aSASLMechanisms, 'SCRAM-SHA3-512', 'SCRAM-SHA-512', 'SCRAM-SHA-256', 'SCRAM-SHA-1'); - } - if ($oConfig->Get('labs', 'sasl_allow_cram_md5', false)) { - $aSASLMechanisms[] = 'CRAM-MD5'; - } - if ($oConfig->Get('labs', 'sasl_allow_plain', true)) { - $aSASLMechanisms[] = 'PLAIN'; - } - $oImapClient->Login([ - 'Login' => $sUsername, - 'Password' => $this->GetActionParam('password', ''), - 'SASLMechanisms' => $aSASLMechanisms - ]); + if (!empty($aAuth['user'])) { + $oSettings->Login = $aAuth['user']; + $oSettings->Password = $aAuth['pass']; + $oImapClient->Login($oSettings); } $oImapClient->Disconnect(); @@ -334,10 +320,15 @@ class ActionsAdmin extends Actions $oSmtpClient->SetLogger($this->Logger()); $oSmtpClient->SetTimeOuts($iConnectionTimeout); - $iTime = \microtime(true); - $oSettings = \MailSo\Net\ConnectSettings::fromArray($oDomain->SmtpSettings()); + $oSettings = $oDomain->SmtpSettings(); $oSmtpClient->Connect($oSettings, \MailSo\Smtp\SmtpClient::EhloHelper()); + if (!empty($aAuth['user'])) { + $oSettings->Login = $aAuth['user']; + $oSettings->Password = $aAuth['pass']; + $oSmtpClient->Login($oSettings); + } + $oSmtpClient->Disconnect(); $bSmtpResult = true; } @@ -365,10 +356,15 @@ class ActionsAdmin extends Actions $oSieveClient->SetLogger($this->Logger()); $oSieveClient->SetTimeOuts($iConnectionTimeout); - $iTime = \microtime(true); - $oSettings = \MailSo\Net\ConnectSettings::fromArray($oDomain->SieveSettings()); + $oSettings = $oDomain->SieveSettings(); $oSieveClient->Connect($oSettings); + if (!empty($aAuth['user'])) { + $oSettings->Login = $aAuth['user']; + $oSettings->Password = $aAuth['pass']; + $oSieveClient->Login($oSettings); + } + $oSieveClient->Disconnect(); $bSieveResult = true; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php index b316ac4c0..b316f6aae 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Api.php @@ -112,33 +112,12 @@ abstract class Api \MailSo\Config::$BoundaryPrefix = \trim(static::Config()->Get('labs', 'boundary_prefix', '')); - $sSslCafile = static::Config()->Get('ssl', 'cafile', ''); - $sSslCapath = static::Config()->Get('ssl', 'capath', ''); - Utils::$CookieDefaultPath = static::Config()->Get('labs', 'cookie_default_path', ''); Utils::$CookieSameSite = static::Config()->Get('security', 'cookie_samesite', 'Strict'); Utils::$CookieSecure = isset($_SERVER['HTTPS']) || 'None' == Utils::$CookieSameSite || !!static::Config()->Get('labs', 'cookie_default_secure', false); - if (!empty($sSslCafile) || !empty($sSslCapath)) - { - \MailSo\Hooks::Add('Net.NetClient.StreamContextSettings/Filter', function ($aStreamContextSettings) use ($sSslCafile, $sSslCapath) { - if (isset($aStreamContextSettings['ssl']) && \is_array($aStreamContextSettings['ssl'])) - { - if (empty($aStreamContextSettings['ssl']['cafile']) && !empty($sSslCafile)) - { - $aStreamContextSettings['ssl']['cafile'] = $sSslCafile; - } - - if (empty($aStreamContextSettings['ssl']['capath']) && !empty($sSslCapath)) - { - $aStreamContextSettings['ssl']['capath'] = $sSslCapath; - } - } - }); - } - \MailSo\Config::$CheckNewMessages = !!static::Config()->Get('labs', 'check_new_messages', true); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 355e19caa..92665084b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -218,12 +218,13 @@ Default is "site=same-origin;site=none"') ), 'ssl' => array( - 'verify_certificate' => array(false, 'Require verification of SSL certificate used.'), - 'allow_self_signed' => array(true, 'Allow self-signed certificates. Requires verify_certificate.'), - 'security_level' => array(1, 'https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html'), - 'cafile' => array('', 'Location of Certificate Authority file on local filesystem (/etc/ssl/certs/ca-certificates.crt)'), - 'capath' => array('', 'capath must be a correctly hashed certificate directory. (/etc/ssl/certs/)'), - 'client_cert' => array('', 'Location of client certificate file (pem format with private key) on local filesystem'), + 'verify_certificate' => array(true, 'Require verification of SSL certificate used.'), + 'allow_self_signed' => array(false, 'Allow self-signed certificates. Requires verify_certificate.'), + 'security_level' => array(1, 'https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html'), + 'cafile' => array('', 'Location of Certificate Authority file on local filesystem (/etc/ssl/certs/ca-certificates.crt)'), + 'capath' => array('', 'capath must be a correctly hashed certificate directory. (/etc/ssl/certs/)'), + 'local_cert' => array('', 'Location of client certificate file (pem format with private key) on local filesystem'), + 'disable_compression' => array(true, 'This can help mitigate the CRIME attack vector.') ), 'capa' => array( diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php index 8ad441a4d..3acb216e6 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Account.php @@ -222,75 +222,48 @@ abstract class Account implements \JsonSerializable $oImapClient->__FORCE_SELECT_ON_EXAMINE__ = !!$oConfig->Get('imap', 'use_force_selection'); $oImapClient->__DISABLE_METADATA = !!$oConfig->Get('imap', 'disable_metadata'); - $aCredentials = \array_merge( - $this->Domain()->ImapSettings(), - array( - 'Login' => $this->IncLogin(), - 'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false), - 'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true), - 'ClientCert' => \trim($oConfig->Get('ssl', 'client_cert', '')) - ) - ); + $oSettings = $this->Domain()->ImapSettings(); + $oSettings->Login = $this->IncLogin(); - $oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, &$aCredentials)); - if ($aCredentials['UseConnect']) { - $oSettings = \MailSo\Net\ConnectSettings::fromArray($aCredentials); - $oImapClient->Connect($oSettings); - } - $oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $aCredentials)); + $oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, $oSettings)); + $oImapClient->Connect($oSettings); + $oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings)); - return $this->netClientLogin($oImapClient, $oConfig, $oPlugins, $aCredentials); + return $this->netClientLogin($oImapClient, $oPlugins, $oSettings); } public function SmtpConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool { - $aCredentials = \array_merge( - $this->Domain()->SmtpSettings(), - array( - 'UseConnect' => !$bUsePhpMail, - 'UsePhpMail' => $bUsePhpMail, - 'Login' => $this->OutLogin(), - 'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false), - 'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true) - ) - ); + $oSettings = $this->Domain()->SmtpSettings(); + $oSettings->Login = $this->OutLogin(); + $oSettings->usePhpMail = $bUsePhpMail; + $oSettings->Ehlo = \MailSo\Smtp\SmtpClient::EhloHelper(); - $oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, &$aCredentials)); - $bUsePhpMail = $aCredentials['UsePhpMail']; - $aCredentials['UseAuth'] = $aCredentials['UseAuth'] && !$aCredentials['UsePhpMail']; + $oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, $oSettings)); + $bUsePhpMail = $oSettings->usePhpMail; + $oSettings->useAuth = $oSettings->useAuth && !$oSettings->usePhpMail; - if ($aCredentials['UseConnect'] && !$aCredentials['UsePhpMail']) { - $oSettings = \MailSo\Net\ConnectSettings::fromArray($aCredentials); - $oSmtpClient->Connect($oSettings, $aCredentials['Ehlo']); + if (!$oSettings->usePhpMail) { + $oSmtpClient->Connect($oSettings, $oSettings->Ehlo); } - $oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $aCredentials)); + $oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $oSettings)); - return $this->netClientLogin($oSmtpClient, $oConfig, $oPlugins, $aCredentials); + return $this->netClientLogin($oSmtpClient, $oPlugins, $oSettings); } public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\ManageSieveClient $oSieveClient, \RainLoop\Config\Application $oConfig) { - $aCredentials = \array_merge( - $this->Domain()->SieveSettings(), - array( - 'Login' => $this->IncLogin(), - 'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false), - 'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true), - 'InitialAuthPlain' => !!$oConfig->Get('labs', 'sieve_auth_plain_initial', true) - ) - ); + $oSettings = $this->Domain()->SieveSettings(); + $oSettings->Login = $this->IncLogin(); - $oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, &$aCredentials)); - if ($aCredentials['UseConnect']) { - $oSettings = \MailSo\Net\ConnectSettings::fromArray($aCredentials); - $oSieveClient->Connect($oSettings); - } - $oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $aCredentials)); + $oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, $oSettings)); + $oSieveClient->Connect($oSettings); + $oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings)); - return $this->netClientLogin($oSieveClient, $oConfig, $oPlugins, $aCredentials); + return $this->netClientLogin($oSieveClient, $oPlugins, $oSettings); } - private function netClientLogin(\MailSo\Net\NetClient $oClient, \RainLoop\Config\Application $oConfig, \RainLoop\Plugins\Manager $oPlugins, array $aCredentials) : bool + private function netClientLogin(\MailSo\Net\NetClient $oClient, \RainLoop\Plugins\Manager $oPlugins, \MailSo\Net\ConnectSettings $oSettings) : bool { /* $encrypted = !empty(\stream_get_meta_data($oClient->ConnectionResource())['crypto']); @@ -301,35 +274,15 @@ abstract class Account implements \JsonSerializable [cipher_version] => TLSv1.3 ) */ - /** - * TODO: move these to Admin -> Domains -> per Domain management? - */ - $aSASLMechanisms = []; - if ($oConfig->Get('labs', 'sasl_allow_scram_sha', false)) { - // https://github.com/the-djmaze/snappymail/issues/182 - \array_push($aSASLMechanisms, 'SCRAM-SHA3-512', 'SCRAM-SHA-512', 'SCRAM-SHA-256', 'SCRAM-SHA-1'); - } - if ($oConfig->Get('labs', 'sasl_allow_cram_md5', false)) { - $aSASLMechanisms[] = 'CRAM-MD5'; - } - if ($oConfig->Get('labs', 'sasl_allow_plain', true)) { - $aSASLMechanisms[] = 'PLAIN'; - } - $aCredentials = \array_merge( - $aCredentials, - array( - 'Password' => $this->Password(), - 'ProxyAuthUser' => $this->ProxyAuthUser(), - 'ProxyAuthPassword' => $this->ProxyAuthPassword(), - 'SASLMechanisms' => $aSASLMechanisms - ) - ); + $oSettings->Password = $this->Password(); + $oSettings->ProxyAuthUser = $this->ProxyAuthUser(); + $oSettings->ProxyAuthPassword = $this->ProxyAuthPassword(); $client_name = \strtolower($oClient->getLogName()); - $oPlugins->RunHook("{$client_name}.before-login", array($this, $oClient, &$aCredentials)); - $bResult = $aCredentials['UseAuth'] && $oClient->Login($aCredentials); - $oPlugins->RunHook("{$client_name}.after-login", array($this, $oClient, $bResult, $aCredentials)); + $oPlugins->RunHook("{$client_name}.before-login", array($this, $oClient, $oSettings)); + $bResult = !$oSettings->useAuth || $oClient->Login($oSettings); + $oPlugins->RunHook("{$client_name}.after-login", array($this, $oClient, $bResult, $oSettings)); return $bResult; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php index 1a34ff223..386d26ca9 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php @@ -4,193 +4,81 @@ namespace RainLoop\Model; use MailSo\Net\Enumerations\ConnectionSecurityType; +// \SnappyMail\IDN::toAscii( +// \SnappyMail\IDN::toUtf8( + class Domain implements \JsonSerializable { - /** - * @var string - */ - private $sName; + private string $Name; - /** - * @var string - */ - private $sIncHost; + private \MailSo\Imap\Settings $IMAP; - /** - * @var int - */ - private $iIncPort; + private \MailSo\Smtp\Settings $SMTP; - /** - * @var int - */ - private $iIncSecure; + private \MailSo\Sieve\Settings $Sieve; - /** - * @var bool - */ - private $bIncShortLogin; + private string $whiteList = ''; - /** - * @var string - */ - private $sOutHost; - - /** - * @var int - */ - private $iOutPort; - - /** - * @var int - */ - private $iOutSecure; - - /** - * @var bool - */ - private $bOutShortLogin; - - /** - * @var bool - */ - private $bOutAuth; - - /** - * @var bool - */ - private $bOutSetSender; - - /** - * @var bool - */ - private $bOutUsePhpMail; - - /** - * @var bool - */ - private $bUseSieve; - - /** - * @var string - */ - private $sSieveHost; - - /** - * @var int - */ - private $iSievePort; - - /** - * @var int - */ - private $iSieveSecure; - - /** - * @var string - */ - private $sWhiteList; - - /** - * @var string - */ - private $sAliasName = ''; + private string $aliasName = ''; function __construct(string $sName) { - $this->sName = $sName; + $this->Name = $sName; + + $this->IMAP = new \MailSo\Imap\Settings; + $this->SMTP = new \MailSo\Smtp\Settings; + $this->Sieve = new \MailSo\Sieve\Settings; } /** - * See ToIniString() for valid values + * Used by old ToIniString() */ public static function fromIniArray(string $sName, array $aDomain) : ?self { $oDomain = null; - - if (\strlen($sName) && \strlen($aDomain['imap_host']) && \strlen($aDomain['imap_port'])) - { + if (\strlen($sName) && \strlen($aDomain['imap_host'])) { $oDomain = new self($sName); - $oDomain->sIncHost = (string) $aDomain['imap_host']; - $oDomain->iIncPort = (int) $aDomain['imap_port']; - $oDomain->iIncSecure = self::StrConnectionSecurityTypeToCons(empty($aDomain['imap_secure']) ? '' : $aDomain['imap_secure']); - $oDomain->bIncShortLogin = !empty($aDomain['imap_short_login']); + $oDomain->IMAP->host = \SnappyMail\IDN::toUtf8($aDomain['imap_host']); + $oDomain->IMAP->port = (int) $aDomain['imap_port']; + $oDomain->IMAP->type = self::StrConnectionSecurityTypeToCons($aDomain['imap_secure'] ?? ''); + $oDomain->IMAP->shortLogin = !empty($aDomain['imap_short_login']); - $oDomain->bUseSieve = !empty($aDomain['sieve_use']); - $oDomain->sSieveHost = empty($aDomain['sieve_host']) ? '' : (string) $aDomain['sieve_host']; - $oDomain->iSievePort = empty($aDomain['sieve_port']) ? 4190 : (int) $aDomain['sieve_port']; - $oDomain->iSieveSecure = self::StrConnectionSecurityTypeToCons(empty($aDomain['sieve_secure']) ? '' : $aDomain['sieve_secure']); + $oDomain->Sieve->enabled = !empty($aDomain['sieve_use']); + $oDomain->Sieve->host = \SnappyMail\IDN::toUtf8($aDomain['sieve_host']); + $oDomain->Sieve->port = (int) ($aDomain['sieve_port'] ?? 4190);; + $oDomain->Sieve->type = self::StrConnectionSecurityTypeToCons($aDomain['sieve_secure'] ?? ''); - $oDomain->sOutHost = empty($aDomain['smtp_host']) ? '' : (string) $aDomain['smtp_host']; - $oDomain->iOutPort = empty($aDomain['smtp_port']) ? 25 : (int) $aDomain['smtp_port']; - $oDomain->iOutSecure = self::StrConnectionSecurityTypeToCons(empty($aDomain['smtp_secure']) ? '' : $aDomain['smtp_secure']); - $oDomain->bOutShortLogin = !empty($aDomain['smtp_short_login']); - $oDomain->bOutAuth = !empty($aDomain['smtp_auth']); - $oDomain->bOutSetSender = !empty($aDomain['smtp_set_sender']); - $oDomain->bOutUsePhpMail = !empty($aDomain['smtp_php_mail']); + $oDomain->SMTP->host = \SnappyMail\IDN::toUtf8($aDomain['smtp_host']); + $oDomain->SMTP->port = (int) ($aDomain['smtp_port'] ?? 25); + $oDomain->SMTP->type = self::StrConnectionSecurityTypeToCons($aDomain['smtp_secure'] ?? ''); + $oDomain->SMTP->shortLogin = !empty($aDomain['smtp_short_login']); + $oDomain->SMTP->useAuth = !empty($aDomain['smtp_auth']); + $oDomain->SMTP->setSender = !empty($aDomain['smtp_set_sender']); + $oDomain->SMTP->usePhpMail = !empty($aDomain['smtp_php_mail']); - $oDomain->sWhiteList = (string) ($aDomain['white_list'] ?? ''); + $oDomain->whiteList = \trim($aDomain['white_list'] ?? ''); + + $oDomain->Normalize(); } - return $oDomain; } - private function encodeIniString(string $sStr) : string - { - return str_replace('"', '\\"', $sStr); - } - public function Normalize() { - $this->sIncHost = \trim($this->sIncHost); - $this->sSieveHost = \trim($this->sSieveHost); - $this->sOutHost = \trim($this->sOutHost); - $this->sWhiteList = \trim($this->sWhiteList); - - if ($this->iIncPort <= 0) - { - $this->iIncPort = 143; - } - - if ($this->iSievePort <= 0) - { - $this->iSievePort = 4190; - } - - if ($this->iOutPort <= 0) - { - $this->iOutPort = 25; - } - } - - public function ToIniString() : string - { - $this->Normalize(); - return \implode("\n", array( - 'imap_host = "'.$this->encodeIniString($this->sIncHost).'"', - 'imap_port = '.$this->iIncPort, - 'imap_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iIncSecure).'"', - 'imap_short_login = '.($this->bIncShortLogin ? 'On' : 'Off'), - 'sieve_use = '.($this->bUseSieve ? 'On' : 'Off'), - 'sieve_host = "'.$this->encodeIniString($this->sSieveHost).'"', - 'sieve_port = '.$this->iSievePort, - 'sieve_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iSieveSecure).'"', - 'smtp_host = "'.$this->encodeIniString($this->sOutHost).'"', - 'smtp_port = '.$this->iOutPort, - 'smtp_secure = "'.self::ConstConnectionSecurityTypeToStr($this->iOutSecure).'"', - 'smtp_short_login = '.($this->bOutShortLogin ? 'On' : 'Off'), - 'smtp_auth = '.($this->bOutAuth ? 'On' : 'Off'), - 'smtp_set_sender = '.($this->bOutSetSender ? 'On' : 'Off'), - 'smtp_php_mail = '.($this->bOutUsePhpMail ? 'On' : 'Off'), - 'white_list = "'.$this->encodeIniString($this->sWhiteList).'"' - )); + $this->IMAP->host = \trim($this->IMAP->host); + $this->Sieve->host = \trim($this->Sieve->host); + $this->SMTP->host = \trim($this->SMTP->host); + $this->whiteList = \trim($this->whiteList); } + /** + * Use by old fromIniArray() + */ public static function StrConnectionSecurityTypeToCons(string $sType) : int { $iSecurityType = ConnectionSecurityType::NONE; - switch (strtoupper($sType)) + switch (\strtoupper($sType)) { case 'SSL': $iSecurityType = ConnectionSecurityType::SSL; @@ -202,22 +90,6 @@ class Domain implements \JsonSerializable return $iSecurityType; } - public static function ConstConnectionSecurityTypeToStr(int $iSecurityType) : string - { - $sType = 'None'; - switch ($iSecurityType) - { - case ConnectionSecurityType::SSL: - $sType = 'SSL'; - break; - case ConnectionSecurityType::STARTTLS: - $sType = 'TLS'; - break; - } - - return $sType; - } - /** * deprecated */ @@ -228,87 +100,89 @@ class Domain implements \JsonSerializable bool $bOutAuth, bool $bOutSetSender, bool $bOutUsePhpMail, string $sWhiteList = '') : self { - $this->sIncHost = $sIncHost; - $this->iIncPort = $iIncPort; - $this->iIncSecure = $iIncSecure; - $this->bIncShortLogin = $bIncShortLogin; + $this->IMAP->host = $sIncHost; + $this->IMAP->port = $iIncPort; + $this->IMAP->type = $iIncSecure; + $this->IMAP->shortLogin = $bIncShortLogin; - $this->bUseSieve = $bUseSieve; - $this->sSieveHost = $sSieveHost; - $this->iSievePort = $iSievePort; - $this->iSieveSecure = $iSieveSecure; + $this->SMTP->host = $sOutHost; + $this->SMTP->port = $iOutPort; + $this->SMTP->type = $iOutSecure; + $this->SMTP->shortLogin = $bOutShortLogin; + $this->SMTP->useAuth = $bOutAuth; + $this->SMTP->setSender = $bOutSetSender; + $this->SMTP->usePhpMail = $bOutUsePhpMail; - $this->sOutHost = $sOutHost; - $this->iOutPort = $iOutPort; - $this->iOutSecure = $iOutSecure; - $this->bOutShortLogin = $bOutShortLogin; - $this->bOutAuth = $bOutAuth; - $this->bOutSetSender = $bOutSetSender; - $this->bOutUsePhpMail = $bOutUsePhpMail; + $this->Sieve->enabled = $bUseSieve; + $this->Sieve->host = $sSieveHost; + $this->Sieve->port = $iSievePort; + $this->Sieve->type = $iSieveSecure; - $this->sWhiteList = \trim($sWhiteList); + $this->whiteList = \trim($sWhiteList); + + $this->Normalize(); return $this; } public function Name() : string { - return $this->sName; + return $this->Name; } public function IncHost() : string { - return $this->sIncHost; + return $this->IMAP->host; } public function IncPort() : int { - return $this->iIncPort; + return $this->IMAP->port; } public function IncShortLogin() : bool { - return $this->bIncShortLogin; + return $this->IMAP->shortLogin; } public function UseSieve() : bool { - return $this->bUseSieve; + return $this->Sieve->enabled; } public function OutHost() : string { - return $this->sOutHost; + return $this->SMTP->host; } public function OutPort() : int { - return $this->iOutPort; + return $this->SMTP->port; } public function OutShortLogin() : bool { - return $this->bOutShortLogin; + return $this->SMTP->shortLogin; } public function OutSetSender() : bool { - return $this->bOutSetSender; + return $this->SMTP->setSender; } public function OutUsePhpMail() : bool { - return $this->bOutUsePhpMail; + return $this->SMTP->usePhpMail; } public function SetAliasName(string $sAliasName) : void { - $this->sAliasName = $sAliasName; + $this->aliasName = $sAliasName; } public function ValidateWhiteList(string $sEmail, string $sLogin = '') : bool { - $sW = \trim($this->sWhiteList); + $sW = \trim($this->whiteList); if (\strlen($sW)) { $sEmail = \MailSo\Base\Utils::IdnToUtf8($sEmail, true); @@ -324,39 +198,36 @@ class Domain implements \JsonSerializable return true; } - public function ImapSettings() : array + public function ImapSettings() : \MailSo\Imap\Settings { - return array( - 'UseConnect' => true, - 'UseAuth' => true, - 'Host' => $this->sIncHost, - 'Port' => $this->iIncPort, - 'Secure' => $this->iIncSecure, - ); + return static::mergeGlobalSettings($this->IMAP); } - public function SmtpSettings() : array + public function SmtpSettings() : \MailSo\Smtp\Settings { - return array( - 'UseConnect' => !$this->bOutUsePhpMail, - 'UseAuth' => $this->bOutAuth, - 'Host' => $this->sOutHost, - 'Port' => $this->iOutPort, - 'Secure' => $this->iOutSecure, - 'Ehlo' => \MailSo\Smtp\SmtpClient::EhloHelper(), - 'UsePhpMail' => $this->bOutUsePhpMail - ); + return static::mergeGlobalSettings($this->SMTP); } - public function SieveSettings() : array + public function SieveSettings() : \MailSo\Sieve\Settings { - return array( - 'UseConnect' => true, - 'UseAuth' => true, - 'Host' => $this->sSieveHost, - 'Port' => $this->iSievePort, - 'Secure' => $this->iSieveSecure - ); + return static::mergeGlobalSettings($this->Sieve); + } + + private static function mergeGlobalSettings(\MailSo\Net\ConnectSettings $oSettings) : \MailSo\Net\ConnectSettings + { + $oConfig = \RainLoop\API::Config(); + if ($oConfig->Get('labs', 'sasl_allow_scram_sha', false)) { + // https://github.com/the-djmaze/snappymail/issues/182 + \array_push($oSettings->SASLMechanisms, 'SCRAM-SHA3-512', 'SCRAM-SHA-512', 'SCRAM-SHA-256', 'SCRAM-SHA-1'); + } + if ($oConfig->Get('labs', 'sasl_allow_cram_md5', false)) { + $oSettings->SASLMechanisms[] = 'CRAM-MD5'; + } + if ($oConfig->Get('labs', 'sasl_allow_plain', true)) { + $oSettings->SASLMechanisms[] = 'PLAIN'; + } + + return $oSettings; } /** @@ -364,30 +235,39 @@ class Domain implements \JsonSerializable */ public static function fromArray(string $sName, array $aDomain) : ?self { - $oDomain = null; - if (\strlen($sName) && \strlen($aDomain['imapHost'])) { - $oDomain = new self($sName); - - $oDomain->sIncHost = \SnappyMail\IDN::toAscii($aDomain['imapHost']); - $oDomain->iIncPort = (int) $aDomain['imapPort']; - $oDomain->iIncSecure = (int) $aDomain['imapSecure']; - $oDomain->bIncShortLogin = !empty($aDomain['imapShortLogin']); - - $oDomain->bUseSieve = !empty($aDomain['useSieve']); - $oDomain->sSieveHost = \SnappyMail\IDN::toAscii($aDomain['sieveHost']); - $oDomain->iSievePort = (int) $aDomain['sievePort']; - $oDomain->iSieveSecure = (int) $aDomain['sieveSecure']; - - $oDomain->sOutHost = \SnappyMail\IDN::toAscii($aDomain['smtpHost']); - $oDomain->iOutPort = (int) $aDomain['smtpPort']; - $oDomain->iOutSecure = (int) $aDomain['smtpSecure']; - $oDomain->bOutShortLogin = !empty($aDomain['smtpShortLogin']); - $oDomain->bOutAuth = !empty($aDomain['smtpAuth']); - $oDomain->bOutSetSender = !empty($aDomain['smtpSetSender']); - $oDomain->bOutUsePhpMail = !empty($aDomain['smtpPhpMail']); - - $oDomain->sWhiteList = (string) $aDomain['whiteList']; + if (!\strlen($sName)) { + return null; } + $oDomain = new self($sName); + if (!empty($aDomain['IMAP'])) { + $oDomain->IMAP = \MailSo\Imap\Settings::fromArray($aDomain['IMAP']); + $oDomain->SMTP = \MailSo\Smtp\Settings::fromArray($aDomain['SMTP']); + $oDomain->Sieve = \MailSo\Sieve\Settings::fromArray($aDomain['Sieve']); + $oDomain->whiteList = (string) $aDomain['whiteList']; + } else if (\strlen($aDomain['imapHost'])) { + $oDomain->IMAP->host = $aDomain['imapHost']; + $oDomain->IMAP->port = (int) $aDomain['imapPort']; + $oDomain->IMAP->type = (int) $aDomain['imapSecure']; + $oDomain->IMAP->shortLogin = !empty($aDomain['imapShortLogin']); + + $oDomain->Sieve->enabled = !empty($aDomain['useSieve']); + $oDomain->Sieve->host = $aDomain['sieveHost']; + $oDomain->Sieve->port = (int) $aDomain['sievePort']; + $oDomain->Sieve->type = (int) $aDomain['sieveSecure']; + + $oDomain->SMTP->host = $aDomain['smtpHost']; + $oDomain->SMTP->port = (int) $aDomain['smtpPort']; + $oDomain->SMTP->type = (int) $aDomain['smtpSecure']; + $oDomain->SMTP->shortLogin = !empty($aDomain['smtpShortLogin']); + $oDomain->SMTP->useAuth = !empty($aDomain['smtpAuth']); + $oDomain->SMTP->setSender = !empty($aDomain['smtpSetSender']); + $oDomain->SMTP->usePhpMail = !empty($aDomain['smtpPhpMail']); + + $oDomain->whiteList = (string) $aDomain['whiteList']; + } else { + return null; + } + $oDomain->Normalize(); return $oDomain; } @@ -396,26 +276,14 @@ class Domain implements \JsonSerializable { $aResult = array( // '@Object' => 'Object/Domain', - 'name' => \SnappyMail\IDN::toUtf8($this->sName), - 'imapHost' => \SnappyMail\IDN::toUtf8($this->sIncHost), - 'imapPort' => $this->iIncPort, - 'imapSecure' => $this->iIncSecure, - 'imapShortLogin' => $this->bIncShortLogin, - 'useSieve' => $this->bUseSieve, - 'sieveHost' => \SnappyMail\IDN::toUtf8($this->sSieveHost), - 'sievePort' => $this->iSievePort, - 'sieveSecure' => $this->iSieveSecure, - 'smtpHost' => \SnappyMail\IDN::toUtf8($this->sOutHost), - 'smtpPort' => $this->iOutPort, - 'smtpSecure' => $this->iOutSecure, - 'smtpShortLogin' => $this->bOutShortLogin, - 'smtpAuth' => $this->bOutAuth, - 'smtpSetSender' => $this->bOutSetSender, - 'smtpPhpMail' => $this->bOutUsePhpMail, - 'whiteList' => $this->sWhiteList + 'name' => $this->Name, + 'IMAP' => $this->IMAP, + 'SMTP' => $this->SMTP, + 'Sieve' => $this->Sieve, + 'whiteList' => $this->whiteList ); - if ($this->sAliasName) { - $aResult['aliasName'] = $this->sAliasName; + if ($this->aliasName) { + $aResult['aliasName'] = $this->aliasName; } return $aResult; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php index 5fcbf11ca..b2cad3769 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php @@ -66,22 +66,10 @@ class Domain extends AbstractProvider throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::DomainAlreadyExists); } return \RainLoop\Model\Domain::fromArray($sNameForTest ?: $sName, [ - 'imapHost' => $oActions->GetActionParam('IncHost', ''), - 'imapPort' => $oActions->GetActionParam('IncPort', 143), - 'imapSecure' => $oActions->GetActionParam('IncSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE), - 'imapShortLogin' => $oActions->GetActionParam('IncShortLogin', 0), - 'useSieve' => $oActions->GetActionParam('UseSieve', 0), - 'sieveHost' => $oActions->GetActionParam('SieveHost', ''), - 'sievePort' => $oActions->GetActionParam('SievePort', 4190), - 'sieveSecure' => $oActions->GetActionParam('SieveSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE), - 'smtpHost' => $oActions->GetActionParam('OutHost', ''), - 'smtpPort' => $oActions->GetActionParam('OutPort', 25), - 'smtpSecure' => $oActions->GetActionParam('OutSecure', \MailSo\Net\Enumerations\ConnectionSecurityType::NONE), - 'smtpShortLogin' => $oActions->GetActionParam('OutShortLogin', 0), - 'smtpAuth' => $oActions->GetActionParam('OutAuth', 1), - 'smtpSetSender' => $oActions->GetActionParam('OutSetSender', 0), - 'smtpPhpMail' => $oActions->GetActionParam('OutUsePhpMail', 0), - 'whiteList' => $oActions->GetActionParam('WhiteList', '') + 'IMAP' => $oActions->GetActionParam('IMAP'), + 'SMTP' => $oActions->GetActionParam('SMTP'), + 'Sieve' => $oActions->GetActionParam('Sieve'), + 'whiteList' => $oActions->GetActionParam('whiteList') ]); } return null; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php index b01d34b2c..7500739e3 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain/DefaultDomain.php @@ -135,7 +135,6 @@ class DefaultDomain implements DomainInterface $this->oCacher->Delete(static::wildcardDomainsCacheKey()); } -// \RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.ini', $oDomain->ToIniString()); \RainLoop\Utils::saveFile($this->sDomainPath.'/'.$sRealFileName.'.json', \json_encode($oDomain, \JSON_PRETTY_PRINT)); return true; diff --git a/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json b/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json index e992a8b82..4cff69f51 100644 --- a/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json +++ b/snappymail/v/0.0.0/app/localization/cs-CZ/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Současné heslo", "LABEL_NEW_PASSWORD": "Nové heslo", "LABEL_REPEAT_PASSWORD": "Znovu", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Vyžadovat ověření SSL certifikátu (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Vyžadovat ověření SSL certifikátu", "LABEL_ALLOW_SELF_SIGNED": "Povolit self-signed certifikáty" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "unstable", "WARNING": "Varování!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/da-DK/admin.json b/snappymail/v/0.0.0/app/localization/da-DK/admin.json index 618fa31e9..3738205c3 100644 --- a/snappymail/v/0.0.0/app/localization/da-DK/admin.json +++ b/snappymail/v/0.0.0/app/localization/da-DK/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Nuværende adgangskode", "LABEL_NEW_PASSWORD": "Ny adgangskode", "LABEL_REPEAT_PASSWORD": "Gentag", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Kræv verifikation af brugt SSL certifikat (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Kræv verifikation af brugt SSL certifikat", "LABEL_ALLOW_SELF_SIGNED": "Tillad selvsignerede certifikater" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "ustabil", "WARNING": "Advarsel!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/de-DE/admin.json b/snappymail/v/0.0.0/app/localization/de-DE/admin.json index 1e3c7af12..3eb59756c 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/admin.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Aktuelles Passwort", "LABEL_NEW_PASSWORD": "Neues Passwort", "LABEL_REPEAT_PASSWORD": "Wiederholen", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Verlangen, dass das verwendete SSL-Zertifikat signiert ist (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Verlangen, dass das verwendete SSL-Zertifikat signiert ist", "LABEL_ALLOW_SELF_SIGNED": "Selbst-signierte Zertifikate erlauben" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "Beta", - "UNSTABLE": "Nicht stabil", "WARNING": "Warnung!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/en/admin.json b/snappymail/v/0.0.0/app/localization/en/admin.json index cf11138be..b8246b803 100644 --- a/snappymail/v/0.0.0/app/localization/en/admin.json +++ b/snappymail/v/0.0.0/app/localization/en/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Current password", "LABEL_NEW_PASSWORD": "New password", "LABEL_REPEAT_PASSWORD": "Repeat", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "unstable", "WARNING": "Warning!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/es-ES/admin.json b/snappymail/v/0.0.0/app/localization/es-ES/admin.json index c0289ad7a..2751bcf33 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/admin.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Contraseña actual", "LABEL_NEW_PASSWORD": "Nueva contraseña", "LABEL_REPEAT_PASSWORD": "Re-ingresar", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Requerir verificación del certificado SSL (IMAP \/ SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Requerir verificación del certificado SSL", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-firmados" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "inestable", "WARNING": "¡Atención!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/fa-IR/admin.json b/snappymail/v/0.0.0/app/localization/fa-IR/admin.json index 19a2179fb..e99b706a9 100644 --- a/snappymail/v/0.0.0/app/localization/fa-IR/admin.json +++ b/snappymail/v/0.0.0/app/localization/fa-IR/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "گذرواژه فعلی", "LABEL_NEW_PASSWORD": "گذرواژه جدید", "LABEL_REPEAT_PASSWORD": "تکرار", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "به بررسی گواهینامه SSL نیاز داشته باشد (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "به بررسی گواهینامه SSL نیاز داشته باشد", "LABEL_ALLOW_SELF_SIGNED": "به گواهی‌نامه‌هایی که توسط خودشان امضاء شده‌اند اجازه بده" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "آزمایشی", - "UNSTABLE": "ناپایدار", "WARNING": "هشدار!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/fi-FI/admin.json b/snappymail/v/0.0.0/app/localization/fi-FI/admin.json index 34fe578c3..2b85e658f 100644 --- a/snappymail/v/0.0.0/app/localization/fi-FI/admin.json +++ b/snappymail/v/0.0.0/app/localization/fi-FI/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Nykyinen salasana", "LABEL_NEW_PASSWORD": "Uusi salasana", "LABEL_REPEAT_PASSWORD": "Toista", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Vaadi SSL sertifikaatin varmistus (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Vaadi SSL sertifikaatin varmistus", "LABEL_ALLOW_SELF_SIGNED": "Salli itse-allekirjoitetut sertifikaatit" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "epävakaa", "WARNING": "Varoitus!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json index 21e3e7aca..e9ad33ff5 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/admin.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Mot de passe actuel", "LABEL_NEW_PASSWORD": "Nouveau mot de passe", "LABEL_REPEAT_PASSWORD": "Répéter", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "S'assurer qu'un certificat SSL (IMAP\/SMTP) est utilisé", + "LABEL_REQUIRE_VERIFICATION": "S'assurer qu'un certificat SSL est utilisé", "LABEL_ALLOW_SELF_SIGNED": "Autoriser les certificats auto-signés" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "bêta", - "UNSTABLE": "instable", "WARNING": "ATTENTION !" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json index 23ad3f153..8ee5e0f3d 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/admin.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Jelenlegi jelszó", "LABEL_NEW_PASSWORD": "Új jelszó", "LABEL_REPEAT_PASSWORD": "Újra", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "A használt (IMAP\/SMTP) tanúsítvány ellenőrzés megkövetelése", + "LABEL_REQUIRE_VERIFICATION": "A használt tanúsítvány ellenőrzés megkövetelése", "LABEL_ALLOW_SELF_SIGNED": "Saját aláírt tanúsítványok engedélyezése" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "béta", - "UNSTABLE": "instabil", "WARNING": "Figyelmeztetés!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/id-ID/admin.json b/snappymail/v/0.0.0/app/localization/id-ID/admin.json index 78ff4b86e..80b5658d6 100644 --- a/snappymail/v/0.0.0/app/localization/id-ID/admin.json +++ b/snappymail/v/0.0.0/app/localization/id-ID/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Password saat ini", "LABEL_NEW_PASSWORD": "Password baru", "LABEL_REPEAT_PASSWORD": "Ulangi", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Diperlukan verifikasi sertifikat SSL dengan (IMAP\/SMTP) ", + "LABEL_REQUIRE_VERIFICATION": "Diperlukan verifikasi sertifikat SSL dengan", "LABEL_ALLOW_SELF_SIGNED": "Izinkan sertifikat yang dibuat sendiri" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "Tidak stabil", "WARNING": "Peringatan!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/it-IT/admin.json b/snappymail/v/0.0.0/app/localization/it-IT/admin.json index 19d598789..e39a6a3b1 100644 --- a/snappymail/v/0.0.0/app/localization/it-IT/admin.json +++ b/snappymail/v/0.0.0/app/localization/it-IT/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Current password", "LABEL_NEW_PASSWORD": "New password", "LABEL_REPEAT_PASSWORD": "Repeat", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "unstable", "WARNING": "Warning!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/ja-JP/admin.json b/snappymail/v/0.0.0/app/localization/ja-JP/admin.json index 053c12aa8..6ac59661c 100644 --- a/snappymail/v/0.0.0/app/localization/ja-JP/admin.json +++ b/snappymail/v/0.0.0/app/localization/ja-JP/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "現在のパスワード", "LABEL_NEW_PASSWORD": "新しいパスワード", "LABEL_REPEAT_PASSWORD": "再入力", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "(IMAP\/SMTP)のSSL証明書の検証を有効にする", + "LABEL_REQUIRE_VERIFICATION": "のSSL証明書の検証を有効にする", "LABEL_ALLOW_SELF_SIGNED": "自己署名証明書を使う" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "unstable", "WARNING": "警告!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/lt-LT/admin.json b/snappymail/v/0.0.0/app/localization/lt-LT/admin.json index 365ce8a1d..00cce1e37 100644 --- a/snappymail/v/0.0.0/app/localization/lt-LT/admin.json +++ b/snappymail/v/0.0.0/app/localization/lt-LT/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Dabartinis slaptažodis", "LABEL_NEW_PASSWORD": "Naujas slaptažodis", "LABEL_REPEAT_PASSWORD": "Pakartokite", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Reikalauti SSL sertifikato (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Reikalauti SSL sertifikato", "LABEL_ALLOW_SELF_SIGNED": "Leisti pačių sukurtus sertifikatus" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "nestabilus", "WARNING": "Dėmesio!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/nb-NO/admin.json b/snappymail/v/0.0.0/app/localization/nb-NO/admin.json index 54d6f5b6e..cd86c17b4 100644 --- a/snappymail/v/0.0.0/app/localization/nb-NO/admin.json +++ b/snappymail/v/0.0.0/app/localization/nb-NO/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Gjeldende passord", "LABEL_NEW_PASSWORD": "Nytt passord", "LABEL_REPEAT_PASSWORD": "Gjenta", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Krev bekreftelse av SSL-sertifikat (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Krev bekreftelse av SSL-sertifikat", "LABEL_ALLOW_SELF_SIGNED": "Tillat selvutstedt sertifikat" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "ustabil", "WARNING": "Advarsel!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json index 6b3c37ab8..0a501d1cf 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/admin.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Huidig wachtwoord", "LABEL_NEW_PASSWORD": "Nieuw wachtwoord", "LABEL_REPEAT_PASSWORD": "Herhaal nieuw wachtwoord", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Verificatie van SSL certificaten (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Verificatie van SSL certificaten", "LABEL_ALLOW_SELF_SIGNED": "Sta zelf ondertekende certificaten toe" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "bèta", - "UNSTABLE": "onstabiel", "WARNING": "Waarschuwing!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/pl-PL/admin.json b/snappymail/v/0.0.0/app/localization/pl-PL/admin.json index c8ff827a2..e23df2592 100644 --- a/snappymail/v/0.0.0/app/localization/pl-PL/admin.json +++ b/snappymail/v/0.0.0/app/localization/pl-PL/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Bieżące hasło", "LABEL_NEW_PASSWORD": "Nowe hasło", "LABEL_REPEAT_PASSWORD": "Powtórz hasło", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Wymagaj weryfikacji używanego certyfikatu SSL (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Wymagaj weryfikacji używanego certyfikatu SSL", "LABEL_ALLOW_SELF_SIGNED": "Zezwól na certyfikaty z podpisem własnym" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "Beta", - "UNSTABLE": "Niestabilne", "WARNING": "Ostrzeżenie!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/pt-BR/admin.json b/snappymail/v/0.0.0/app/localization/pt-BR/admin.json index 6fc8db410..dba7cfe84 100644 --- a/snappymail/v/0.0.0/app/localization/pt-BR/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt-BR/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Senha atual", "LABEL_NEW_PASSWORD": "Nova senha", "LABEL_REPEAT_PASSWORD": "Confirmar nova senha", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Exigir verificação do certificado SSL (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Exigir verificação do certificado SSL", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados auto-assinados" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "instável", "WARNING": "Aviso!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/pt-PT/admin.json b/snappymail/v/0.0.0/app/localization/pt-PT/admin.json index cef446e7f..2888da31f 100644 --- a/snappymail/v/0.0.0/app/localization/pt-PT/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt-PT/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Palavra-passe atual", "LABEL_NEW_PASSWORD": "Nova palavra-passe", "LABEL_REPEAT_PASSWORD": "Repetir", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Obrigar a verificação dos certificados SSL utilizados (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Obrigar a verificação dos certificados SSL utilizados", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados autoassinados" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "instável", "WARNING": "Aviso!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/pt/admin.json b/snappymail/v/0.0.0/app/localization/pt/admin.json index cef446e7f..2888da31f 100644 --- a/snappymail/v/0.0.0/app/localization/pt/admin.json +++ b/snappymail/v/0.0.0/app/localization/pt/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Palavra-passe atual", "LABEL_NEW_PASSWORD": "Nova palavra-passe", "LABEL_REPEAT_PASSWORD": "Repetir", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Obrigar a verificação dos certificados SSL utilizados (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Obrigar a verificação dos certificados SSL utilizados", "LABEL_ALLOW_SELF_SIGNED": "Permitir certificados autoassinados" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "instável", "WARNING": "Aviso!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/ru-RU/admin.json b/snappymail/v/0.0.0/app/localization/ru-RU/admin.json index 0c788d5ea..cf4dbaf98 100644 --- a/snappymail/v/0.0.0/app/localization/ru-RU/admin.json +++ b/snappymail/v/0.0.0/app/localization/ru-RU/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Текущий пароль", "LABEL_NEW_PASSWORD": "Новый пароль", "LABEL_REPEAT_PASSWORD": "Повторить пароль", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Требовать проверку SSL сертификата для IMAP и SMTP", + "LABEL_REQUIRE_VERIFICATION": "Требовать проверку SSL сертификата для", "LABEL_ALLOW_SELF_SIGNED": "Разрешить cамоподписанные сертификаты" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "unstable", "WARNING": "Внимание!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/sk-SK/admin.json b/snappymail/v/0.0.0/app/localization/sk-SK/admin.json index d473cea39..fe87cd306 100644 --- a/snappymail/v/0.0.0/app/localization/sk-SK/admin.json +++ b/snappymail/v/0.0.0/app/localization/sk-SK/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Súčasné heslo", "LABEL_NEW_PASSWORD": "Nové heslo", "LABEL_REPEAT_PASSWORD": "Opakovať", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Require verification of SSL certificate used", "LABEL_ALLOW_SELF_SIGNED": "Allow self signed certificates" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "nestabilné", "WARNING": "Upozornenie!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/sl-SI/admin.json b/snappymail/v/0.0.0/app/localization/sl-SI/admin.json index 4a257fbe3..0c4038169 100644 --- a/snappymail/v/0.0.0/app/localization/sl-SI/admin.json +++ b/snappymail/v/0.0.0/app/localization/sl-SI/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Trenutno geslo", "LABEL_NEW_PASSWORD": "Novo geslo", "LABEL_REPEAT_PASSWORD": "Ponovno", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Obvezno overjanje uporabljenega SSL certifikata (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Obvezno overjanje uporabljenega SSL certifikata", "LABEL_ALLOW_SELF_SIGNED": "Dovoli samopodpisane certifikate" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "nestabilno", "WARNING": "Pozor!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json index 95682f0b7..d9d7d60c0 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/admin.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Nuvarande lösenord", "LABEL_NEW_PASSWORD": "Nytt lösenord", "LABEL_REPEAT_PASSWORD": "Upprepa", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "Kräver verifiering av SSL-certifikat som används (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Kräver verifiering av SSL-certifikat som används", "LABEL_ALLOW_SELF_SIGNED": "Låt självsignerade certifikat" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "beta", - "UNSTABLE": "instabil", "WARNING": "Varning!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/vi-VN/admin.json b/snappymail/v/0.0.0/app/localization/vi-VN/admin.json index 27c1906bd..82e851cfb 100644 --- a/snappymail/v/0.0.0/app/localization/vi-VN/admin.json +++ b/snappymail/v/0.0.0/app/localization/vi-VN/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "Mật khẩu hiện tại", "LABEL_NEW_PASSWORD": "Mật khẩu mới", "LABEL_REPEAT_PASSWORD": "Lập lại mật khẩu", - "LEGEND_SSL": "Chứng chỉ số SSL", - "LABEL_REQUIRE_VERIFICATION": "Yêu cầu xác minh chứng chỉ SSL được dùng (IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "Yêu cầu xác minh chứng chỉ SSL được dùng", "LABEL_ALLOW_SELF_SIGNED": "Cho phép chứng chỉ tự ký" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "đang thử nghiệm", - "UNSTABLE": "chưa ổn định", "WARNING": "Cảnh báo!" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json index bf9a519ae..c8308a1f0 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/admin.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/admin.json @@ -88,8 +88,7 @@ "LABEL_CURRENT_PASSWORD": "当前密码", "LABEL_NEW_PASSWORD": "新密码", "LABEL_REPEAT_PASSWORD": "重复输入新密码", - "LEGEND_SSL": "SSL", - "LABEL_REQUIRE_VERIFICATION": "要求验证 SSL 证书(IMAP\/SMTP)", + "LABEL_REQUIRE_VERIFICATION": "要求验证 SSL 证书", "LABEL_ALLOW_SELF_SIGNED": "允许自签名证书" }, "TAB_PACKAGES": { @@ -158,7 +157,6 @@ }, "HINTS": { "BETA": "测试版本", - "UNSTABLE": "不稳定版本", "WARNING": "警告" }, "ERRORS": { diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html index 32f3e8dc4..65d93e872 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsSecurity.html @@ -58,7 +58,7 @@
-
+
SSL/TLS
- ()
()
-
@@ -18,8 +18,8 @@
- -
- -
-
+

-

-
- +
@@ -169,8 +207,8 @@
- - + +
-
+
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html index 861e25999..ea69fe3cb 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html @@ -109,6 +109,13 @@ value: pgpEncrypt } }">
+
From 58e9ebc37b9e8690934d5d64255cec380b5792a5 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 12:45:08 +0100 Subject: [PATCH 034/114] WIP: Added attachmentListSimple for #672 This is not optional yet and off. --- dev/Styles/User/Attachments.less | 19 +++++++++++++++++++ dev/Styles/User/MessageView.less | 4 +--- dev/View/User/MailBox/MessageView.js | 3 ++- .../templates/Views/User/MailMessageView.html | 14 +++++++++++++- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/dev/Styles/User/Attachments.less b/dev/Styles/User/Attachments.less index 6f8108397..14693e551 100644 --- a/dev/Styles/User/Attachments.less +++ b/dev/Styles/User/Attachments.less @@ -120,3 +120,22 @@ cursor: pointer; } } + +.attachmentListSimple { + margin: 0; + + .attachmentItem { + min-height: 1em; + border: 1px solid var(--border-color); + padding: 0 2px; + } + + .attachmentName { + margin:0; + } + + .checkboxAttachment { + position: initial; + margin-left:.5em; + } +} diff --git a/dev/Styles/User/MessageView.less b/dev/Styles/User/MessageView.less index 9ba14a9c3..76a78bf86 100644 --- a/dev/Styles/User/MessageView.less +++ b/dev/Styles/User/MessageView.less @@ -95,7 +95,6 @@ html.rl-no-preview-pane { } .messageTags { - margin-top: 6px; align-items: baseline; display: flex; } @@ -128,7 +127,6 @@ html.rl-no-preview-pane { display: flex; font-size: 18px; font-weight: bold; - margin-bottom: 8px; } .subject { @@ -148,7 +146,7 @@ html.rl-no-preview-pane { } .informationShort { - margin: 4px 40px 0 0; + margin: 0 40px 0 0; overflow: hidden; text-overflow: ellipsis; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 8d1bd6bd3..02671db9d 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -98,6 +98,7 @@ export class MailMessageView extends AbstractViewRight { downloadAsZipLoading: false, showFullInfo: '1' === Local.get(ClientSideKeyNameMessageHeaderFullInfo), moreDropdownTrigger: false, + attachmentListSimple: false, // viewer viewFromShort: '', @@ -281,7 +282,7 @@ export class MailMessageView extends AbstractViewRight { } } - el = eqs(event, '.attachmentsPlace .attachmentItem .attachmentNameParent'); + el = eqs(event, '.attachmentsPlace .attachmentName'); if (el) { const attachment = ko.dataFor(el); if (attachment?.linkDownload()) { diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html index 4680ad695..20323f49e 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -232,6 +232,7 @@
+
-
+
+ + + + From b47035ef433dde94f27caa411c79ca3660bdc1ad Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 13:13:52 +0100 Subject: [PATCH 035/114] Activate "Simple attachments list" for #672 --- dev/Settings/User/General.js | 4 ++-- dev/Stores/User/Settings.js | 2 ++ dev/View/User/MailBox/MessageView.js | 2 +- snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php | 2 ++ .../v/0.0.0/app/libraries/RainLoop/Actions/User.php | 1 + snappymail/v/0.0.0/app/localization/ar-SA/user.json | 3 ++- snappymail/v/0.0.0/app/localization/bg-BG/user.json | 3 ++- snappymail/v/0.0.0/app/localization/cs-CZ/user.json | 3 ++- snappymail/v/0.0.0/app/localization/da-DK/user.json | 3 ++- snappymail/v/0.0.0/app/localization/de-DE/user.json | 3 ++- snappymail/v/0.0.0/app/localization/el-GR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/en-GB/user.json | 3 ++- snappymail/v/0.0.0/app/localization/en/user.json | 3 ++- snappymail/v/0.0.0/app/localization/es-ES/user.json | 3 ++- snappymail/v/0.0.0/app/localization/et-EE/user.json | 3 ++- snappymail/v/0.0.0/app/localization/fa-IR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/fi-FI/user.json | 3 ++- snappymail/v/0.0.0/app/localization/fr-FR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/hu-HU/user.json | 3 ++- snappymail/v/0.0.0/app/localization/id-ID/user.json | 3 ++- snappymail/v/0.0.0/app/localization/is-IS/user.json | 3 ++- snappymail/v/0.0.0/app/localization/it-IT/user.json | 3 ++- snappymail/v/0.0.0/app/localization/ja-JP/user.json | 3 ++- snappymail/v/0.0.0/app/localization/ko-KR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/lt-LT/user.json | 3 ++- snappymail/v/0.0.0/app/localization/lv-LV/user.json | 3 ++- snappymail/v/0.0.0/app/localization/nb-NO/user.json | 3 ++- snappymail/v/0.0.0/app/localization/nl-NL/user.json | 3 ++- snappymail/v/0.0.0/app/localization/pl-PL/user.json | 3 ++- snappymail/v/0.0.0/app/localization/pt-BR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/pt-PT/user.json | 3 ++- snappymail/v/0.0.0/app/localization/pt/user.json | 3 ++- snappymail/v/0.0.0/app/localization/ro-RO/user.json | 3 ++- snappymail/v/0.0.0/app/localization/ru-RU/user.json | 3 ++- snappymail/v/0.0.0/app/localization/sk-SK/user.json | 3 ++- snappymail/v/0.0.0/app/localization/sl-SI/user.json | 3 ++- snappymail/v/0.0.0/app/localization/sv-SE/user.json | 3 ++- snappymail/v/0.0.0/app/localization/tr-TR/user.json | 3 ++- snappymail/v/0.0.0/app/localization/uk-UA/user.json | 3 ++- snappymail/v/0.0.0/app/localization/vi-VN/user.json | 3 ++- snappymail/v/0.0.0/app/localization/zh-CN/user.json | 3 ++- snappymail/v/0.0.0/app/localization/zh-TW/user.json | 3 ++- .../0.0.0/app/templates/Views/User/MailMessageView.html | 9 +++++---- .../0.0.0/app/templates/Views/User/SettingsGeneral.html | 7 +++++++ 44 files changed, 94 insertions(+), 44 deletions(-) diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index e9cac6b20..f2e1430e6 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -42,7 +42,7 @@ export class UserSettingsGeneral extends AbstractViewSettings { ['layout', 'messageReadDelay', 'messagesPerPage', 'editorDefaultType', 'requestReadReceipt', 'requestDsn', 'pgpSign', 'pgpEncrypt', - 'viewHTML', 'showImages', 'removeColors', 'hideDeleted', 'listInlineAttachments', + 'viewHTML', 'showImages', 'removeColors', 'hideDeleted', 'listInlineAttachments', 'simpleAttachmentsList', 'useCheckboxesInList', 'useThreads', 'replySameFolder', 'msgDefaultAction', 'allowSpellcheck' ].forEach(name => this[name] = SettingsUserStore[name]); @@ -97,7 +97,7 @@ export class UserSettingsGeneral extends AbstractViewSettings { this.addSetting('MessagesPerPage'); this.addSetting('Layout'); - this.addSettings(['ViewHTML', 'ShowImages', 'HideDeleted', 'ListInlineAttachments', + this.addSettings(['ViewHTML', 'ShowImages', 'HideDeleted', 'ListInlineAttachments', 'simpleAttachmentsList', 'UseCheckboxesInList', 'ReplySameFolder', 'requestReadReceipt', 'requestDsn', 'pgpSign', 'pgpEncrypt', 'allowSpellcheck', 'DesktopNotifications', 'SoundNotification']); diff --git a/dev/Stores/User/Settings.js b/dev/Stores/User/Settings.js index b0fb4aef0..c6398f9b1 100644 --- a/dev/Stores/User/Settings.js +++ b/dev/Stores/User/Settings.js @@ -19,6 +19,7 @@ export const SettingsUserStore = new class { showImages: 0, removeColors: 0, listInlineAttachments: 0, + simpleAttachmentsList: 0, useCheckboxesInList: 1, allowDraftAutosave: 1, useThreads: 0, @@ -80,6 +81,7 @@ export const SettingsUserStore = new class { self.showImages(SettingsGet('ShowImages')); self.removeColors(SettingsGet('RemoveColors')); self.listInlineAttachments(SettingsGet('ListInlineAttachments')); + self.simpleAttachmentsList(SettingsGet('simpleAttachmentsList')); self.useCheckboxesInList(SettingsGet('UseCheckboxesInList')); self.allowDraftAutosave(SettingsGet('AllowDraftAutosave')); self.useThreads(SettingsGet('UseThreads')); diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 02671db9d..1ab430df3 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -92,13 +92,13 @@ export class MailMessageView extends AbstractViewRight { }, this.messageVisibility); this.msgDefaultAction = SettingsUserStore.msgDefaultAction; + this.simpleAttachmentsList = SettingsUserStore.simpleAttachmentsList; addObservablesTo(this, { showAttachmentControls: !!Local.get(ClientSideKeyNameMessageAttachmentControls), downloadAsZipLoading: false, showFullInfo: '1' === Local.get(ClientSideKeyNameMessageHeaderFullInfo), moreDropdownTrigger: false, - attachmentListSimple: false, // viewer viewFromShort: '', diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 11c14d508..32faf6442 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -695,6 +695,7 @@ class Actions 'ShowImages' => (bool) $oConfig->Get('defaults', 'show_images', false), 'RemoveColors' => (bool) $oConfig->Get('defaults', 'remove_colors', false), 'ListInlineAttachments' => false, + 'simpleAttachmentsList' => false, 'MessagesPerPage' => (int) $oConfig->Get('webmail', 'messages_per_page', 25), 'MessageReadDelay' => (int) $oConfig->Get('webmail', 'message_read_delay', 5), 'MsgDefaultAction' => 1, @@ -794,6 +795,7 @@ class Actions $aResult['ShowImages'] = (bool)$oSettings->GetConf('ShowImages', $aResult['ShowImages']); $aResult['RemoveColors'] = (bool)$oSettings->GetConf('RemoveColors', $aResult['RemoveColors']); $aResult['ListInlineAttachments'] = (bool)$oSettings->GetConf('ListInlineAttachments', $aResult['ListInlineAttachments']); + $aResult['simpleAttachmentsList'] = (bool)$oSettings->GetConf('simpleAttachmentsList', $aResult['simpleAttachmentsList']); $aResult['ContactsAutosave'] = (bool)$oSettings->GetConf('ContactsAutosave', $aResult['ContactsAutosave']); $aResult['MessagesPerPage'] = (int)$oSettings->GetConf('MessagesPerPage', $aResult['MessagesPerPage']); $aResult['MessageReadDelay'] = (int)$oSettings->GetConf('MessageReadDelay', $aResult['MessageReadDelay']); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php index c4ebce3ed..9f0e0b574 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -340,6 +340,7 @@ trait User $this->setSettingsFromParams($oSettings, 'ShowImages', 'bool'); $this->setSettingsFromParams($oSettings, 'RemoveColors', 'bool'); $this->setSettingsFromParams($oSettings, 'ListInlineAttachments', 'bool'); + $this->setSettingsFromParams($oSettings, 'simpleAttachmentsList', 'bool'); $this->setSettingsFromParams($oSettings, 'ContactsAutosave', 'bool'); $this->setSettingsFromParams($oSettings, 'DesktopNotifications', 'bool'); $this->setSettingsFromParams($oSettings, 'SoundNotification', 'bool'); diff --git a/snappymail/v/0.0.0/app/localization/ar-SA/user.json b/snappymail/v/0.0.0/app/localization/ar-SA/user.json index 5db32d4be..ca725bdd8 100644 --- a/snappymail/v/0.0.0/app/localization/ar-SA/user.json +++ b/snappymail/v/0.0.0/app/localization/ar-SA/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "صوت التنبيهات", "CHROME_NOTIFICATION_DESC": "إظهار نافذة منبثقة في حالة قدوم ايميل جديد", "CHROME_NOTIFICATION_DESC_DENIED": "(تم إعتراضها من قبل المتصفح)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "قم باضافة المستلمين تلقائيا الى دفتر العناوين", diff --git a/snappymail/v/0.0.0/app/localization/bg-BG/user.json b/snappymail/v/0.0.0/app/localization/bg-BG/user.json index 7bc884236..0387c2e04 100644 --- a/snappymail/v/0.0.0/app/localization/bg-BG/user.json +++ b/snappymail/v/0.0.0/app/localization/bg-BG/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Звуково известяване", "CHROME_NOTIFICATION_DESC": "Покажи известие при ново съобщение", "CHROME_NOTIFICATION_DESC_DENIED": "(Блокирано от браузъра)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Автоматично добавяне на получателите към адресната ви книга", diff --git a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json index f5d13c1fb..710bf9b83 100644 --- a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json +++ b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Zvukové notifikace", "CHROME_NOTIFICATION_DESC": "Zobrazovat upozornění na nové zprávy", "CHROME_NOTIFICATION_DESC_DENIED": "(Zablokované prohlížečem)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automaticky přidávat adresy příjemců do Adresáře", diff --git a/snappymail/v/0.0.0/app/localization/da-DK/user.json b/snappymail/v/0.0.0/app/localization/da-DK/user.json index 302914b62..e38f7e96d 100644 --- a/snappymail/v/0.0.0/app/localization/da-DK/user.json +++ b/snappymail/v/0.0.0/app/localization/da-DK/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Lyd notifikationer", "CHROME_NOTIFICATION_DESC": "Vis ny meddelelses notifikations popup", "CHROME_NOTIFICATION_DESC_DENIED": "(Blokeret af browseren)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Tilføj automatisk modtager til adressebog", diff --git a/snappymail/v/0.0.0/app/localization/de-DE/user.json b/snappymail/v/0.0.0/app/localization/de-DE/user.json index 33ffaa413..d137ff824 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/user.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Benachrichtigungston", "CHROME_NOTIFICATION_DESC": "Popups einblenden, wenn neue Nachrichten vorhanden sind", "CHROME_NOTIFICATION_DESC_DENIED": "(Vom Browser blockiert.)", - "LIST_INLINE_ATTACHMENTS": "Inline-Anhänge in der Liste anzeigen" + "LIST_INLINE_ATTACHMENTS": "Inline-Anhänge in der Liste anzeigen", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Empfänger automatisch zu Ihrem Adressbuch hinzuzufügen", diff --git a/snappymail/v/0.0.0/app/localization/el-GR/user.json b/snappymail/v/0.0.0/app/localization/el-GR/user.json index a7d4b98c9..b078346fc 100644 --- a/snappymail/v/0.0.0/app/localization/el-GR/user.json +++ b/snappymail/v/0.0.0/app/localization/el-GR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Ηχητική επισήμανση", "CHROME_NOTIFICATION_DESC": "Να εμφανίζονται τα νέα μηνύματα σε αναδυόμενες επισημάνσεις", "CHROME_NOTIFICATION_DESC_DENIED": "(Αποκλείστηκε από το φυλλομετρητή)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Αυτόματη προσθήκη επαφών στον κατάλογο διευθύνσεων", diff --git a/snappymail/v/0.0.0/app/localization/en-GB/user.json b/snappymail/v/0.0.0/app/localization/en-GB/user.json index 457b02098..a4f02da73 100644 --- a/snappymail/v/0.0.0/app/localization/en-GB/user.json +++ b/snappymail/v/0.0.0/app/localization/en-GB/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Show new messages notification popups", "CHROME_NOTIFICATION_DESC_DENIED": "(Blocked by the browser)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatically add recipients to your address book", diff --git a/snappymail/v/0.0.0/app/localization/en/user.json b/snappymail/v/0.0.0/app/localization/en/user.json index ef4e9eacf..0b91cef9d 100644 --- a/snappymail/v/0.0.0/app/localization/en/user.json +++ b/snappymail/v/0.0.0/app/localization/en/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Show new messages notification popups", "CHROME_NOTIFICATION_DESC_DENIED": "(Blocked by the browser)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatically add recipients to your address book", diff --git a/snappymail/v/0.0.0/app/localization/es-ES/user.json b/snappymail/v/0.0.0/app/localization/es-ES/user.json index dbfdf144c..0b158dab2 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/user.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sonido de la notificación", "CHROME_NOTIFICATION_DESC": "Mostrar notificación de nuevos mensajes en Popup", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloqueado por el explorador)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Agregar automáticamente destinatarios a la libreta de direcciones", diff --git a/snappymail/v/0.0.0/app/localization/et-EE/user.json b/snappymail/v/0.0.0/app/localization/et-EE/user.json index d3b1d6cec..9fd65a483 100644 --- a/snappymail/v/0.0.0/app/localization/et-EE/user.json +++ b/snappymail/v/0.0.0/app/localization/et-EE/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Teavitused heliga", "CHROME_NOTIFICATION_DESC": "Näita uute kirjade teavitust hüpikaknaga", "CHROME_NOTIFICATION_DESC_DENIED": "(Veebilehitseja poolt blokeeritud)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Lisa saajad automaatselt aadressiraamatusse", diff --git a/snappymail/v/0.0.0/app/localization/fa-IR/user.json b/snappymail/v/0.0.0/app/localization/fa-IR/user.json index 9ad54c0bf..99c6e5bbb 100644 --- a/snappymail/v/0.0.0/app/localization/fa-IR/user.json +++ b/snappymail/v/0.0.0/app/localization/fa-IR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "آگاه‌سازی با صدا", "CHROME_NOTIFICATION_DESC": "پنجره اعلان دریافت پیام جدید را نمایش بده", "CHROME_NOTIFICATION_DESC_DENIED": "(مسدود شده توسط مرورگر)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "اطلاعات تماس گیرندگان را بصورت خودکار در آدرس‌بوک ذخیره کن", diff --git a/snappymail/v/0.0.0/app/localization/fi-FI/user.json b/snappymail/v/0.0.0/app/localization/fi-FI/user.json index ba6da76a2..036ad01ea 100644 --- a/snappymail/v/0.0.0/app/localization/fi-FI/user.json +++ b/snappymail/v/0.0.0/app/localization/fi-FI/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Äänimerkit", "CHROME_NOTIFICATION_DESC": "Näytä uusien viestien ponnahdusikkuna", "CHROME_NOTIFICATION_DESC_DENIED": "(Estetty selaimessa)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Lisää yhetystieto automaattisesti osoitekirjaan", diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/user.json b/snappymail/v/0.0.0/app/localization/fr-FR/user.json index 50ed7438f..f11f3f06e 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/user.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Son de notification", "CHROME_NOTIFICATION_DESC": "Afficher un pop-up de notification pour les nouveaux messages", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloqué par le navigateur)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Ajouter automatiquement les destinataires à votre carnet d'adresses", diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/user.json b/snappymail/v/0.0.0/app/localization/hu-HU/user.json index 863e95055..1704fa9f9 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/user.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Hangos értesítés", "CHROME_NOTIFICATION_DESC": "Új üzenetek értesítő ablakainak megjelenítése", "CHROME_NOTIFICATION_DESC_DENIED": "(a böngésző blokkolta)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Címzettek automatikus hozzáadása a címtárhoz", diff --git a/snappymail/v/0.0.0/app/localization/id-ID/user.json b/snappymail/v/0.0.0/app/localization/id-ID/user.json index 428cb5487..2645efdc9 100644 --- a/snappymail/v/0.0.0/app/localization/id-ID/user.json +++ b/snappymail/v/0.0.0/app/localization/id-ID/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notifikasi suara", "CHROME_NOTIFICATION_DESC": "Tampilkan jendela sembul notifikasi pesan", "CHROME_NOTIFICATION_DESC_DENIED": "(diblokir oleh browser)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Tambah penerima ke daftar alamat secara otomatis", diff --git a/snappymail/v/0.0.0/app/localization/is-IS/user.json b/snappymail/v/0.0.0/app/localization/is-IS/user.json index d0a6ae06b..c01abbe86 100644 --- a/snappymail/v/0.0.0/app/localization/is-IS/user.json +++ b/snappymail/v/0.0.0/app/localization/is-IS/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Tilkynningarhljóð", "CHROME_NOTIFICATION_DESC": "Sýna tilkynningarglugga fyrir ný skilaboð", "CHROME_NOTIFICATION_DESC_DENIED": "(Lokað af vafra)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Bæta viðtakendum sjálfkrafa í nafnaskrá", diff --git a/snappymail/v/0.0.0/app/localization/it-IT/user.json b/snappymail/v/0.0.0/app/localization/it-IT/user.json index 9605ced7b..7ad06fe51 100644 --- a/snappymail/v/0.0.0/app/localization/it-IT/user.json +++ b/snappymail/v/0.0.0/app/localization/it-IT/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notifica sonora", "CHROME_NOTIFICATION_DESC": "Mostra le notifiche in caso di nuovi messaggi", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloccate dal browser)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Aggiungi automaticamente le persone che ti inviano mail alla rubrica", diff --git a/snappymail/v/0.0.0/app/localization/ja-JP/user.json b/snappymail/v/0.0.0/app/localization/ja-JP/user.json index 7534d9fa5..b32e8ed5b 100644 --- a/snappymail/v/0.0.0/app/localization/ja-JP/user.json +++ b/snappymail/v/0.0.0/app/localization/ja-JP/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "サウンド通知", "CHROME_NOTIFICATION_DESC": "新しいメッセージを受信したらポップアップで知らせる", "CHROME_NOTIFICATION_DESC_DENIED": "(ブラウザでブロックされています)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "返信したアドレスをアドレス帳へ自動的に追加する", diff --git a/snappymail/v/0.0.0/app/localization/ko-KR/user.json b/snappymail/v/0.0.0/app/localization/ko-KR/user.json index 95d82d77f..7451fc0d0 100644 --- a/snappymail/v/0.0.0/app/localization/ko-KR/user.json +++ b/snappymail/v/0.0.0/app/localization/ko-KR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "소리 알림", "CHROME_NOTIFICATION_DESC": "새 메시지 알림 팝업 사용", "CHROME_NOTIFICATION_DESC_DENIED": "(브라우저에 의해 차단됨)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "수신인을 주소록에 자동으로 추가", diff --git a/snappymail/v/0.0.0/app/localization/lt-LT/user.json b/snappymail/v/0.0.0/app/localization/lt-LT/user.json index ed93c6c90..84f5e84cb 100644 --- a/snappymail/v/0.0.0/app/localization/lt-LT/user.json +++ b/snappymail/v/0.0.0/app/localization/lt-LT/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Garsinis pranešimas", "CHROME_NOTIFICATION_DESC": "Rodyti iššokančius pranešimus apie naujas žinutes", "CHROME_NOTIFICATION_DESC_DENIED": "(Blokuoja naršyklė)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatiškai pridėti gavėjus į adresų knygą", diff --git a/snappymail/v/0.0.0/app/localization/lv-LV/user.json b/snappymail/v/0.0.0/app/localization/lv-LV/user.json index e3e26066c..21d45ba96 100644 --- a/snappymail/v/0.0.0/app/localization/lv-LV/user.json +++ b/snappymail/v/0.0.0/app/localization/lv-LV/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Rādīt jaunus paziņojumus kā izlecošus logus", "CHROME_NOTIFICATION_DESC_DENIED": "(Parlūkprogramma bloķēja)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatically add recipients to your address book", diff --git a/snappymail/v/0.0.0/app/localization/nb-NO/user.json b/snappymail/v/0.0.0/app/localization/nb-NO/user.json index 97a69b0fa..029943f2f 100644 --- a/snappymail/v/0.0.0/app/localization/nb-NO/user.json +++ b/snappymail/v/0.0.0/app/localization/nb-NO/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Lydvarsel", "CHROME_NOTIFICATION_DESC": "Vis sprettopp-varsel om nye meldinger", "CHROME_NOTIFICATION_DESC_DENIED": "(blokkert av nettleser)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Legg til mottakere i adresseboka automatisk", diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/user.json b/snappymail/v/0.0.0/app/localization/nl-NL/user.json index a7e0a3276..f460e51c3 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/user.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notificatie geluid", "CHROME_NOTIFICATION_DESC": "Toon nieuwe berichten popup", "CHROME_NOTIFICATION_DESC_DENIED": "(Geblokkeerd door de browser)", - "LIST_INLINE_ATTACHMENTS": "Toon inline bijlagen in lijst" + "LIST_INLINE_ATTACHMENTS": "Toon inline bijlagen in lijst", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatisch ontvangers toevoegen aan uw adresboek", diff --git a/snappymail/v/0.0.0/app/localization/pl-PL/user.json b/snappymail/v/0.0.0/app/localization/pl-PL/user.json index 8db5c1751..5bfc17f1f 100644 --- a/snappymail/v/0.0.0/app/localization/pl-PL/user.json +++ b/snappymail/v/0.0.0/app/localization/pl-PL/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Powiadomienia dźwiękowe", "CHROME_NOTIFICATION_DESC": "Wyświetlaj powiadomienia o nowych wiadomościach", "CHROME_NOTIFICATION_DESC_DENIED": "(Zablokowane przez przeglądarkę)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automatycznie dodawaj odbiorców do książki adresowej", diff --git a/snappymail/v/0.0.0/app/localization/pt-BR/user.json b/snappymail/v/0.0.0/app/localization/pt-BR/user.json index 48cedb07a..ba185bbe8 100644 --- a/snappymail/v/0.0.0/app/localization/pt-BR/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-BR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notificação sonora", "CHROME_NOTIFICATION_DESC": "Mostrar notificação popup para novas mensagens", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloqueado pelo navegador)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Adicionar automaticamente os destinatários na lista de endereços", diff --git a/snappymail/v/0.0.0/app/localization/pt-PT/user.json b/snappymail/v/0.0.0/app/localization/pt-PT/user.json index 96e30e86c..345886cb4 100644 --- a/snappymail/v/0.0.0/app/localization/pt-PT/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-PT/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notificações sonoras", "CHROME_NOTIFICATION_DESC": "Apresentar notificações para novas mensagens", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloqueado pelo navegador)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Adicionar destinatários automaticamente à sua lista de endereços", diff --git a/snappymail/v/0.0.0/app/localization/pt/user.json b/snappymail/v/0.0.0/app/localization/pt/user.json index 96e30e86c..345886cb4 100644 --- a/snappymail/v/0.0.0/app/localization/pt/user.json +++ b/snappymail/v/0.0.0/app/localization/pt/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Notificações sonoras", "CHROME_NOTIFICATION_DESC": "Apresentar notificações para novas mensagens", "CHROME_NOTIFICATION_DESC_DENIED": "(Bloqueado pelo navegador)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Adicionar destinatários automaticamente à sua lista de endereços", diff --git a/snappymail/v/0.0.0/app/localization/ro-RO/user.json b/snappymail/v/0.0.0/app/localization/ro-RO/user.json index 36d5214b3..e0ccad867 100644 --- a/snappymail/v/0.0.0/app/localization/ro-RO/user.json +++ b/snappymail/v/0.0.0/app/localization/ro-RO/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Afișați notificări desktop pentru mesaje noi", "CHROME_NOTIFICATION_DESC_DENIED": "(browser-ul blocat)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Adaugă automat destinatarii la agenda de scrisori", diff --git a/snappymail/v/0.0.0/app/localization/ru-RU/user.json b/snappymail/v/0.0.0/app/localization/ru-RU/user.json index 8973391b4..bd0f2cdf1 100644 --- a/snappymail/v/0.0.0/app/localization/ru-RU/user.json +++ b/snappymail/v/0.0.0/app/localization/ru-RU/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Звуковые уведомления", "CHROME_NOTIFICATION_DESC": "Показывать уведомления о новых сообщениях в всплывающих подсказках", "CHROME_NOTIFICATION_DESC_DENIED": "(Блокировано браузером)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Автоматически добавлять получателей писем в адресную книгу", diff --git a/snappymail/v/0.0.0/app/localization/sk-SK/user.json b/snappymail/v/0.0.0/app/localization/sk-SK/user.json index cdd2b212d..95486aa95 100644 --- a/snappymail/v/0.0.0/app/localization/sk-SK/user.json +++ b/snappymail/v/0.0.0/app/localization/sk-SK/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Zobrazovať upozornenia na nové správy", "CHROME_NOTIFICATION_DESC_DENIED": "(Zablokované prehliadačom)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Automaticky pridávať príjemcov správ do Adresára", diff --git a/snappymail/v/0.0.0/app/localization/sl-SI/user.json b/snappymail/v/0.0.0/app/localization/sl-SI/user.json index b742ce99e..80b315dcb 100644 --- a/snappymail/v/0.0.0/app/localization/sl-SI/user.json +++ b/snappymail/v/0.0.0/app/localization/sl-SI/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Zvočno obvestilo", "CHROME_NOTIFICATION_DESC": "Prikaži obvestila ob novih sporočilih", "CHROME_NOTIFICATION_DESC_DENIED": "(Zavrnil brskalnik)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Samodejno dodaj prejemnike v imenik", diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/user.json b/snappymail/v/0.0.0/app/localization/sv-SE/user.json index 9ff61c100..e0ecc7729 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/user.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Ljudnotifikation", "CHROME_NOTIFICATION_DESC": "Visa nya meddelande-aviseringar som popup-fönster", "CHROME_NOTIFICATION_DESC_DENIED": "(Blockat av webbläsaren)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Lägg till mottagare automatiskt i din adressbok", diff --git a/snappymail/v/0.0.0/app/localization/tr-TR/user.json b/snappymail/v/0.0.0/app/localization/tr-TR/user.json index 6cdbc7881..c53a5a038 100644 --- a/snappymail/v/0.0.0/app/localization/tr-TR/user.json +++ b/snappymail/v/0.0.0/app/localization/tr-TR/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "Yeni mesajları pop-up ile göster", "CHROME_NOTIFICATION_DESC_DENIED": "(Tarayıcınız tarafından engellendi)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Otomatik olarak adres defterinize alıcıyı ekle", diff --git a/snappymail/v/0.0.0/app/localization/uk-UA/user.json b/snappymail/v/0.0.0/app/localization/uk-UA/user.json index aa99d1767..4a3616a98 100644 --- a/snappymail/v/0.0.0/app/localization/uk-UA/user.json +++ b/snappymail/v/0.0.0/app/localization/uk-UA/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Звукове сповіщення", "CHROME_NOTIFICATION_DESC": "Показувати сповіщення про нові повідомлення у підказках", "CHROME_NOTIFICATION_DESC_DENIED": "(Заблоковано браузером)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Автоматично додавати отримувачів у адресну книгу", diff --git a/snappymail/v/0.0.0/app/localization/vi-VN/user.json b/snappymail/v/0.0.0/app/localization/vi-VN/user.json index 9d0594ff9..025d88a90 100644 --- a/snappymail/v/0.0.0/app/localization/vi-VN/user.json +++ b/snappymail/v/0.0.0/app/localization/vi-VN/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Thông báo âm thanh", "CHROME_NOTIFICATION_DESC": "Hiện thị thông báo nẩy lên nhanh khi có thư mới", "CHROME_NOTIFICATION_DESC_DENIED": "(Đang bị chặn bởi trình duyệt web)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Tự động thêm người nhận thư vào sổ địa chỉ của bạn", diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/user.json b/snappymail/v/0.0.0/app/localization/zh-CN/user.json index f8dd60ef2..bd51798dc 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "提示音", "CHROME_NOTIFICATION_DESC": "显示新邮件弹窗", "CHROME_NOTIFICATION_DESC_DENIED": "(被浏览器阻止)", - "LIST_INLINE_ATTACHMENTS": "在列表中显示内联附件" + "LIST_INLINE_ATTACHMENTS": "在列表中显示内联附件", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "自动添加到您的地址簿", diff --git a/snappymail/v/0.0.0/app/localization/zh-TW/user.json b/snappymail/v/0.0.0/app/localization/zh-TW/user.json index 3d649adec..2f167ae31 100644 --- a/snappymail/v/0.0.0/app/localization/zh-TW/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-TW/user.json @@ -412,7 +412,8 @@ "SOUND_NOTIFICATION": "Sound notification", "CHROME_NOTIFICATION_DESC": "顯示新郵件彈窗", "CHROME_NOTIFICATION_DESC_DENIED": "(被瀏覽器阻止)", - "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list" + "LIST_INLINE_ATTACHMENTS": "Show inline attachments in list", + "LIST_SIMPLE_ATTACHMENTS": "Simple attachments list" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "自動添加到你的位址簿", diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html index 20323f49e..7c265a668 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -227,12 +227,10 @@
-
- +
  • @@ -260,7 +258,7 @@
- +
  • @@ -284,6 +282,9 @@ data-i18n="MESSAGE/LINK_DOWNLOAD_AS_ZIP">
+ +
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html index ea69fe3cb..d2d91d289 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsGeneral.html @@ -165,6 +165,13 @@ value: listInlineAttachments } }">
+
-
+
+
  • -
+
From 81a453955ee9a9c8234d5810973f5fd7ef96b21d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 13:37:25 +0100 Subject: [PATCH 037/114] v2.21.2 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 27 +++++++++++++++++++ README.md | 26 +++++++++--------- dev/Knoin/Knoin.js | 4 +-- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- plugins/demo-account/storage.php | 5 ++++ 10 files changed, 53 insertions(+), 21 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 3bf3267de..5ded294e7 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.21.1" +current = "2.21.2" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index e52b26952..60ad0fa61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +## 2.21.2 – 2022-11-15 + +### Added +- Allow browser Spellchecker + [#574](https://github.com/the-djmaze/snappymail/pull/574) +- Decode MIME charset of .EML attachments + [#662](https://github.com/the-djmaze/snappymail/pull/662) + +### Changed +- Increase message visible text area + [#672](https://github.com/the-djmaze/snappymail/pull/672) +- When copy/paste image use the raw data instead of clipboard HTML + [#654](https://github.com/the-djmaze/snappymail/pull/654) +- When application.ini debug.enable is true, also debug js and css +- JavaScript rl.setWindowTitle() renamed to rl.setTitle() + +### Removed +- Message toggle fullscreen button which was only in mobile view + +### Nextcloud +- Workaround Nextcloud calendar crashes + [#622](https://github.com/the-djmaze/snappymail/pull/622) + [#661](https://github.com/the-djmaze/snappymail/pull/661) +- Added share public/internal file link + [#569](https://github.com/the-djmaze/snappymail/pull/569) + + ## 2.21.1 – 2022-11-13 ### Fixed diff --git a/README.md b/README.md index 460b60820..89c9940d4 100644 --- a/README.md +++ b/README.md @@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.170.153 | 82.515 | -|app.js |4.207.787 | 406.190 | -|boot.js | 868.735 | 2.031 | +|admin.js |2.170.153 | 82.504 | +|app.js |4.207.787 | 406.789 | +|boot.js | 868.735 | 1.989 | |libs.js | 658.812 | 192.965 | |sieve.js | 0 | 86.121 | |polyfills.js | 334.608 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.240.095 | 770.107 | +|TOTAL |8.240.095 | 770.653 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 256.831 | 40.856 | 73.606 | 13.566 | 60.877 | 12.142 | -|app.min.js | 515.367 | 188.137 |139.456 | 62.532 |110.485 | 53.603 | -|boot.min.js | 84.659 | 1.251 | 26.998 | 773 | 23.643 | 627 | -|libs.min.js | 584.772 | 92.055 |180.901 | 34.113 |155.182 | 30.641 | +|admin.min.js | 256.831 | 40.844 | 73.606 | 13.561 | 60.877 | 12.134 | +|app.min.js | 515.367 | 188.622 |139.456 | 62.735 |110.485 | 53.807 | +|boot.min.js | 84.659 | 1.216 | 26.998 | 761 | 23.643 | 614 | +|libs.min.js | 584.772 | 92.042 |180.901 | 34.110 |155.182 | 30.627 | |sieve.min.js | 0 | 41.926 | 0 | 10.484 | 0 | 9.451 | |polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 | -|TOTAL user |1.217.635 | 281.443 |358.761 | 97.418 |299.485 | 84.871 | -|TOTAL user+sieve |1.217.635 | 323.369 |358.761 |107.902 |299.485 | 94.322 | -|TOTAL admin | 959.099 | 134.162 |292.911 | 48.452 |249.877 | 43.410 | +|TOTAL user |1.217.635 | 281.880 |358.761 | 97.606 |299.485 | 85.048 | +|TOTAL user+sieve |1.217.635 | 323.806 |358.761 |108.090 |299.485 | 94.499 | +|TOTAL admin | 959.099 | 134.102 |292.911 | 48.432 |249.877 | 43.375 | For a user its around 70% smaller and faster than traditional RainLoop. @@ -187,8 +187,8 @@ For a user its around 70% smaller and faster than traditional RainLoop. |css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli | |------------ |-------: |------: |------: |------: |--------: | -|app.css | 340.331 | 83.746 | 46.946 | 17.262 | 14.779 | -|app.min.css | 274.947 | 67.483 | 39.647 | 15.311 | 13.365 | +|app.css | 340.331 | 83.444 | 46.946 | 17.239 | 14.750 | +|app.min.css | 274.947 | 67.244 | 39.647 | 15.279 | 13.333 | |boot.css | | 1.326 | | 664 | 545 | |boot.min.css | | 1.071 | | 590 | 474 | |admin.css | | 30.816 | | 6.939 | 6.028 | diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index 2a40c47f5..8636c7166 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -35,8 +35,6 @@ const dialog = ViewTypePopup === vm.viewType, vmPlace = doc.getElementById(position); - fireEvent('rl-view-model.create', vm); - ViewModelClass.__builded = true; ViewModelClass.__vm = vm; @@ -113,6 +111,8 @@ const vmDom.addEventListener('transitionend', endShowHide); } + fireEvent('rl-view-model.create', vm); + ko.applyBindingAccessorsToNode( vmDom, { diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index a86230c55..34570f1c6 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.21.1. +This app packages SnappyMail 2.21.2. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 29eeb6b3f..4b34c17c4 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.21.1 +VERSION=2.21.2 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index fc49e08c9..797556124 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.21.1 + 2.21.2 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli sDataPath}/demo.pgp/.gnupg/private-keys-v1.d/3106F4281F98D820114228FEF16B5BA0D78AA005.key")); + \file_put_contents("{$sDataPath}/.gnupg/private-keys-v1.d/82CA239C482423D364BFD6DFC3E400B3B98AD66F.key",file_get_contents("{$this->sDataPath}/demo.pgp/.gnupg/private-keys-v1.d/82CA239C482423D364BFD6DFC3E400B3B98AD66F.key")); + // ECC \file_put_contents("{$sDataPath}/.gnupg/private-keys-v1.d/5A1A6C7310D0508C68E8E74F15068301E83FD1AE.key",file_get_contents("{$this->sDataPath}/demo.pgp/.gnupg/private-keys-v1.d/5A1A6C7310D0508C68E8E74F15068301E83FD1AE.key")); \file_put_contents("{$sDataPath}/.gnupg/private-keys-v1.d/886921A7E06BE56F8E8C51797BB476BB26DF21BF.key",file_get_contents("{$this->sDataPath}/demo.pgp/.gnupg/private-keys-v1.d/886921A7E06BE56F8E8C51797BB476BB26DF21BF.key")); + \file_put_contents("{$sDataPath}/.gnupg/pubring.kbx",file_get_contents("{$this->sDataPath}/demo.pgp/.gnupg/pubring.kbx")); \file_put_contents("{$sDataPath}/.gnupg/trustdb.gpg",file_get_contents("{$this->sDataPath}/demo.pgp/.gnupg/trustdb.gpg")); } From 9fbae7f07e04bc745a6fec3e040c714f6c65cfba Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 14:02:30 +0100 Subject: [PATCH 038/114] Resolve #672 --- .../templates/Views/User/MailMessageView.html | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html index 5925baf36..f32f3ef46 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -131,28 +131,9 @@ ×
-
-
- - -
- () -
-
-
-
- : - -
-
- : - -
-
-
- : - -
+
+ +
@@ -196,28 +177,47 @@
-
- -
- : - -
- 🏷 - -
-
  • - -
  • -
    - -
    -
    -
    -
    +
    +
    + +
    +
    + : + +
    +
    + : + +
    +
    +
    +
    + : + +
    +
    + +
    + : + +
    + 🏷 + +
    +
  • + +
  • +
    + +
    +
    +
    + +
    From 9cda088645a734f5597f716e3a5e84d1db3f240a Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 14:13:13 +0100 Subject: [PATCH 039/114] Improved message spam score detailed view --- .../app/templates/Views/User/MailMessageView.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html index f32f3ef46..56f40994a 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -167,9 +167,9 @@ () - - - % + + + @@ -192,10 +192,10 @@
    -
    -
    - : - +
    + : + +
    From 676d379e29f24e3f734fbc44b00dc73884f0d5af Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 14:45:38 +0100 Subject: [PATCH 040/114] Resolve #501 --- dev/View/Popup/Compose.js | 17 ++++++----------- .../app/templates/Views/User/PopupsCompose.html | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 06306527d..d36a73729 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -272,15 +272,11 @@ export class ComposePopupView extends AbstractViewPopup { currentIdentity: IdentityUserStore()[0] }); - // this.to.subscribe((v) => console.log(v)); - // Used by ko.bindingHandlers.emailsTags - this.to.focused = ko.observable(false); - this.to.focused.subscribe(value => value && (this.sLastFocusedField = 'to')); - this.cc.focused = ko.observable(false); - this.cc.focused.subscribe(value => value && (this.sLastFocusedField = 'cc')); - this.bcc.focused = ko.observable(false); - this.bcc.focused.subscribe(value => value && (this.sLastFocusedField = 'bcc')); + ['to','cc','bcc'].forEach(name => { + this[name].focused = ko.observable(false); + this[name].focused.subscribe(value => value && (this.sLastFocusedField = name)); + }); this.attachments = koArrayWithDestroy(); @@ -1014,9 +1010,8 @@ export class ComposePopupView extends AbstractViewPopup { setTimeout(() => { if (!this.to()) { this.to.focused(true); - // TODO https://github.com/the-djmaze/snappymail/issues/501#issuecomment-1255906243 -// } else if (!this.subject()) { -// this.subject.focus(); + } else if (!this.subject()) { + this.viewModelDom.querySelector('input[name="subject"]').focus(); } else { this.oEditor?.focus(); } diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsCompose.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsCompose.html index 84084de4b..acda0b238 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsCompose.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsCompose.html @@ -124,7 +124,7 @@
    - + From c1b33dc0c43fa302fc12f858bf7d842c92719c19 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 14:46:51 +0100 Subject: [PATCH 041/114] Use fontastic in Nextcloud Files selector dialog --- plugins/nextcloud/js/webdav.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/nextcloud/js/webdav.js b/plugins/nextcloud/js/webdav.js index 07dde6cba..9915dd2b8 100644 --- a/plugins/nextcloud/js/webdav.js +++ b/plugins/nextcloud/js/webdav.js @@ -156,19 +156,22 @@ const li.dataset.icon = '🗎'; btn.name = 'select'; - btn.textContent = '📎 attach'; + btn.dataset.icon = '📎'; + btn.textContent = 'attach'; btn.className = 'button-vue'; li.append(btn); btn = document.createElement('button'); btn.name = 'share-internal'; - btn.textContent = '🔗 internal'; + btn.dataset.icon = '🔗'; + btn.textContent = 'internal'; btn.className = 'button-vue'; li.append(btn); btn = document.createElement('button'); btn.name = 'share-public'; - btn.textContent = '🔗 public'; + btn.dataset.icon = '🔗'; + btn.textContent = 'public'; btn.className = 'button-vue'; li.append(btn); From b926d86568a5d376b48d5836e42637ced2517058 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 23:56:50 +0100 Subject: [PATCH 042/114] Resolve #678 --- dev/Common/Html.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/Common/Html.js b/dev/Common/Html.js index 2ffa1edf4..66a7f647a 100644 --- a/dev/Common/Html.js +++ b/dev/Common/Html.js @@ -234,7 +234,7 @@ export const if (!value) { value = getAttribute('width'); if (1 < pInt(value)) { - oStyle.width = value.includes('%') ? value : value + 'px'; + value = oStyle.width = value.includes('%') ? value : value + 'px'; } } delAttribute('width'); From 565c8b13faf4e7006279804554aa6072c728e14f Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 15 Nov 2022 23:57:39 +0100 Subject: [PATCH 043/114] Better solution for #678 --- dev/Common/Html.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/Common/Html.js b/dev/Common/Html.js index 66a7f647a..be4b54335 100644 --- a/dev/Common/Html.js +++ b/dev/Common/Html.js @@ -233,8 +233,9 @@ export const if (hasAttribute('width')) { if (!value) { value = getAttribute('width'); + value = value.includes('%') ? value : value + 'px'; if (1 < pInt(value)) { - value = oStyle.width = value.includes('%') ? value : value + 'px'; + oStyle.width = value; } } delAttribute('width'); From ee5fb1a83e6ca140c555d14cec29e9b029a9f746 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 08:42:28 +0100 Subject: [PATCH 044/114] Improved message responsive resizing #678 --- dev/Common/Html.js | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/dev/Common/Html.js b/dev/Common/Html.js index be4b54335..427247b5a 100644 --- a/dev/Common/Html.js +++ b/dev/Common/Html.js @@ -229,33 +229,28 @@ export const // if ('TABLE' === name || 'TD' === name || 'TH' === name) { if (!oStyle.backgroundImage) { - value = oStyle.width; - if (hasAttribute('width')) { - if (!value) { + if ('TD' !== name && 'TH' !== name) { + // Make width responsive + if (hasAttribute('width')) { value = getAttribute('width'); - value = value.includes('%') ? value : value + 'px'; - if (1 < pInt(value)) { - oStyle.width = value; - } + oStyle.width = value.includes('%') ? value : value + 'px'; + delAttribute('width'); } - delAttribute('width'); - } - if (value && !value.includes('%')) { - oStyle.maxWidth = value; - if ('TD' !== name && 'TH' !== name) { + value = oStyle.removeProperty('width'); + if (value) { + oStyle.maxWidth = value; oStyle.width = '100%'; } - } - if (hasAttribute('height')) { - value = getAttribute('height'); - if (pInt(value)) { + // Make height responsive + if (hasAttribute('height')) { + value = getAttribute('height'); oStyle.height = value.includes('%') ? value : value + 'px'; + delAttribute('height'); + } + value = oStyle.removeProperty('height'); + if (value) { + oStyle.maxHeight = value; } - delAttribute('height'); - } - value = oStyle.removeProperty('height'); - if (value && !value.includes('%')) { - oStyle.maxHeight = value; } } // } else From f4448635d1046275e80c0e259d8615a77dace3cb Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 15:14:00 +0100 Subject: [PATCH 045/114] Bugfix handling attachments MIME type / content-type as it was broken. --- dev/Common/File.js | 3 +- dev/Model/ComposeAttachment.js | 3 +- dev/View/Popup/Compose.js | 71 +++-- dev/View/Popup/OpenPgpImport.js | 4 +- dev/View/User/MailBox/MessageView.js | 36 ++- .../0.0.0/app/libraries/MailSo/Base/Utils.php | 175 +---------- .../app/libraries/MailSo/Mime/Attachment.php | 46 +-- .../0.0.0/app/libraries/RainLoop/Actions.php | 28 +- .../libraries/RainLoop/Actions/Messages.php | 73 +++-- .../app/libraries/RainLoop/Actions/Raw.php | 14 +- .../libraries/snappymail/file/magic.mime.php | 34 +++ .../libraries/snappymail/file/mimetype.php | 276 ++++++++++++++++++ 12 files changed, 461 insertions(+), 302 deletions(-) create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/file/magic.mime.php create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php diff --git a/dev/Common/File.js b/dev/Common/File.js index 9cf850106..acd5a370e 100644 --- a/dev/Common/File.js +++ b/dev/Common/File.js @@ -22,7 +22,8 @@ const ics: 'text/calendar', xml: 'text/xml', json: app+'json', - asc: app+'pgp-signature', +// asc: app+'pgp-signature', +// asc: app+'pgp-keys', p10: app+'pkcs10', p7c: app+'pkcs7-mime', p7m: app+'pkcs7-mime', diff --git a/dev/Model/ComposeAttachment.js b/dev/Model/ComposeAttachment.js index 540d947c1..5b7798628 100644 --- a/dev/Model/ComposeAttachment.js +++ b/dev/Model/ComposeAttachment.js @@ -27,6 +27,7 @@ export class ComposeAttachmentModel extends AbstractModel { fileName: fileName, size: size, tempName: '', + type: '', // application/octet-stream progress: 0, error: '', @@ -54,7 +55,7 @@ export class ComposeAttachmentModel extends AbstractModel { return null === localSize ? '' : FileInfo.friendlySize(localSize); }, - mimeType: () => FileInfo.getContentType(this.fileName()), + mimeType: () => this.type() || FileInfo.getContentType(this.fileName()), fileExt: () => FileInfo.getExtension(this.fileName()), iconClass: () => FileInfo.getIconClass(this.fileExt(), this.mimeType()) diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index d36a73729..d17098331 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -11,7 +11,7 @@ import { SetSystemFoldersNotification } from 'Common/EnumsUser'; -import { pInt, isArray, arrayLength, forEachObjectEntry } from 'Common/Utils'; +import { pInt, isArray, arrayLength } from 'Common/Utils'; import { encodeHtml, HtmlEditor, htmlToPlain } from 'Common/Html'; import { koArrayWithDestroy, addObservablesTo, addComputablesTo, addSubscribablesTo } from 'External/ko'; @@ -973,28 +973,22 @@ export class ComposePopupView extends AbstractViewPopup { if (arrayLength(downloads)) { Remote.request('MessageUploadAttachments', (iError, oData) => { - if (!iError) { - forEachObjectEntry(oData.Result, (tempName, id) => { - const attachment = this.getAttachmentById(id); - if (attachment) { - attachment.tempName(tempName); - attachment - .waiting(false) - .uploading(false) - .complete(true); + const result = oData?.Result; + downloads.forEach((id, index) => { + const attachment = this.getAttachmentById(id); + if (attachment) { + attachment + .waiting(false) + .uploading(false) + .complete(true); + if (iError || !result?.[index]) { + attachment.error(getUploadErrorDescByCode(UploadErrorCode.NoFileUploaded)); + } else { + attachment.tempName(result[index].TempName); + attachment.type(result[index].MimeType); } - }); - } else { - this.attachments.forEach(attachment => { - if (attachment?.fromMessage) { - attachment - .waiting(false) - .uploading(false) - .complete(true) - .error(getUploadErrorDescByCode(UploadErrorCode.NoFileUploaded)); - } - }); - } + } + }); }, { Attachments: downloads @@ -1123,6 +1117,7 @@ export class ComposePopupView extends AbstractViewPopup { attachment.size(attachmentJson.Size ? pInt(attachmentJson.Size) : 0); attachment.tempName(attachmentJson.TempName ? attachmentJson.TempName : ''); attachment.isInline = false; + attachment.type(attachmentJson.MimeType); } } }); @@ -1178,20 +1173,6 @@ export class ComposePopupView extends AbstractViewPopup { return this.attachments.find(item => item && id === item.id); } - /** - * @returns {Object} - */ - prepareAttachmentsForSendOrSave() { - const result = {}; - this.attachments.forEach(item => { - if (item?.complete() && item?.tempName() && item?.enabled()) { - result[item.tempName()] = [item.fileName(), item.isInline ? '1' : '0', item.CID, item.contentLocation]; - } - }); - - return result; - } - /** * @param {MessageModel} message */ @@ -1237,6 +1218,7 @@ export class ComposePopupView extends AbstractViewPopup { if (message) { let reply = [ComposeType.Reply, ComposeType.ReplyAll].includes(type); if (reply || [ComposeType.Forward, ComposeType.Draft, ComposeType.EditAsNew].includes(type)) { + // item instanceof AttachmentModel message.attachments.forEach(item => { if (!reply || item.isLinked()) { const attachment = new ComposeAttachmentModel( @@ -1249,6 +1231,7 @@ export class ComposePopupView extends AbstractViewPopup { item.contentLocation ); attachment.fromMessage = true; + attachment.type(item.mimeType); this.addAttachment(attachment); } }); @@ -1405,6 +1388,20 @@ export class ComposePopupView extends AbstractViewPopup { async getMessageRequestParams(sSaveFolder, draft) { + // Prepare ComposeAttachmentModel attachments + const attachments = {}; + this.attachments.forEach(item => { + if (item?.complete() && item?.tempName() && item?.enabled()) { + attachments[item.tempName()] = { + name: item.fileName(), + inline: item.isInline, + cid: item.CID, + location: item.contentLocation, + type: item.mimeType() + }; + } + }); + const identity = this.currentIdentity(), params = { @@ -1422,7 +1419,7 @@ export class ComposePopupView extends AbstractViewPopup { InReplyTo: this.sInReplyTo, References: this.sReferences, MarkAsImportant: this.markAsImportant() ? 1 : 0, - Attachments: this.prepareAttachmentsForSendOrSave(), + Attachments: attachments, // Only used at send, not at save: Dsn: this.requestDsn() ? 1 : 0, ReadReceiptRequest: this.requestReadReceipt() ? 1 : 0 diff --git a/dev/View/Popup/OpenPgpImport.js b/dev/View/Popup/OpenPgpImport.js index f8c43951b..30b80f0e2 100644 --- a/dev/View/Popup/OpenPgpImport.js +++ b/dev/View/Popup/OpenPgpImport.js @@ -69,8 +69,8 @@ export class OpenPgpImportPopupView extends AbstractViewPopup { this.close(); } - onShow() { - this.key(''); + onShow(key) { + this.key(key || ''); this.keyError(false); this.keyErrorMessage(''); } diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 1ab430df3..0c7e90cf7 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -55,10 +55,17 @@ import { MimeToMessage } from 'Mime/Utils'; import { MessageModel } from 'Model/Message'; +import { showScreenPopup } from 'Knoin/Knoin'; +import { OpenPgpImportPopupView } from 'View/Popup/OpenPgpImport'; +import { GnuPGUserStore } from 'Stores/User/GnuPG'; +import { OpenPGPUserStore } from 'Stores/User/OpenPGP'; + const oMessageScrollerDom = () => elementById('messageItem') || {}, - currentMessage = MessageUserStore.message; + currentMessage = MessageUserStore.message, + + fetchRaw = url => rl.fetch(url).then(response => response.ok && response.text()); export class MailMessageView extends AbstractViewRight { constructor() { @@ -284,22 +291,23 @@ export class MailMessageView extends AbstractViewRight { el = eqs(event, '.attachmentsPlace .attachmentName'); if (el) { - const attachment = ko.dataFor(el); - if (attachment?.linkDownload()) { - if ('message/rfc822' == attachment.mimeType) { + const attachment = ko.dataFor(el), url = attachment?.linkDownload(); + if (url) { + if ('application/pgp-keys' == attachment.mimeType + && (OpenPGPUserStore.isSupported() || GnuPGUserStore.isSupported())) { + fetchRaw(url).then(text => + showScreenPopup(OpenPgpImportPopupView, [text]) + ); + } else if ('message/rfc822' == attachment.mimeType) { // TODO - rl.fetch(attachment.linkDownload()).then(response => { - if (response.ok) { - response.text().then(text => { - const oMessage = new MessageModel(); - MimeToMessage(text, oMessage); - // cleanHTML - oMessage.viewPopupMessage(); - }); - } + fetchRaw(url).then(text => { + const oMessage = new MessageModel(); + MimeToMessage(text, oMessage); + // cleanHTML + oMessage.viewPopupMessage(); }); } else { - download(attachment.linkDownload(), attachment.fileName); + download(url, attachment.fileName); } } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php index aedc2625b..fa77a5ee5 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Base/Utils.php @@ -501,180 +501,6 @@ abstract class Utils return false === $iLast ? '' : \strtolower(\substr($sFileName, $iLast + 1)); } - public static function MimeContentType(string $sFileName) : string - { - $sResult = 'application/octet-stream'; - $sFileName = \trim(\strtolower($sFileName)); - - if ('winmail.dat' === $sFileName) - { - return 'application/ms-tnef'; - } - - $aMimeTypes = array( - - 'eml' => 'message/rfc822', - 'mime' => 'message/rfc822', - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'def' => 'text/plain', - 'list' => 'text/plain', - 'in' => 'text/plain', - 'ini' => 'text/plain', - 'log' => 'text/plain', - 'sql' => 'text/plain', - 'cfg' => 'text/plain', - 'conf' => 'text/plain', - 'rtx' => 'text/richtext', - 'vcard' => 'text/vcard', - 'vcf' => 'text/vcard', - 'htm' => 'text/html', - 'html' => 'text/html', - 'csv' => 'text/csv', - 'ics' => 'text/calendar', - 'ifb' => 'text/calendar', - 'xml' => 'text/xml', - 'json' => 'application/json', - 'swf' => 'application/x-shockwave-flash', - 'hlp' => 'application/winhlp', - 'wgt' => 'application/widget', - 'chm' => 'application/vnd.ms-htmlhelp', - 'asc' => 'application/pgp-signature', - 'p10' => 'application/pkcs10', - 'p7c' => 'application/pkcs7-mime', - 'p7m' => 'application/pkcs7-mime', - 'p7s' => 'application/pkcs7-signature', - 'torrent' => 'application/x-bittorrent', - - // scripts - 'js' => 'application/javascript', - 'pl' => 'text/perl', - 'css' => 'text/css', - 'asp' => 'text/asp', - 'php' => 'application/x-php', - - // images - 'png' => 'image/png', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'jfif' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'cgm' => 'image/cgm', - 'ief' => 'image/ief', - 'ico' => 'image/x-icon', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'djv' => 'image/vnd.djvu', - 'djvu' => 'image/vnd.djvu', - 'webp' => 'image/webp', - - // archives - 'zip' => 'application/zip', - '7z' => 'application/x-7z-compressed', - 'rar' => 'application/x-rar-compressed', - 'exe' => 'application/x-msdownload', - 'dll' => 'application/x-msdownload', - 'scr' => 'application/x-msdownload', - 'com' => 'application/x-msdownload', - 'bat' => 'application/x-msdownload', - 'msi' => 'application/x-msdownload', - 'cab' => 'application/vnd.ms-cab-compressed', - 'gz' => 'application/x-gzip', - 'tgz' => 'application/x-gzip', - 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', - 'deb' => 'application/x-debian-package', - 'tar' => 'application/x-tar', - - // fonts - 'psf' => 'application/x-font-linux-psf', - 'otf' => 'application/x-font-otf', - 'pcf' => 'application/x-font-pcf', - 'snf' => 'application/x-font-snf', - 'ttf' => 'application/x-font-ttf', - 'ttc' => 'application/x-font-ttf', - - // audio - 'aac' => 'audio/aac', - 'flac' => 'audio/flac', - 'mp3' => 'audio/mpeg', - 'aif' => 'audio/aiff', - 'aifc' => 'audio/aiff', - 'aiff' => 'audio/aiff', - 'wav' => 'audio/x-wav', - 'midi' => 'audio/midi', - 'mp4a' => 'audio/mp4', - 'ogg' => 'audio/ogg', - 'weba' => 'audio/webm', - 'm3u' => 'audio/x-mpegurl', - - // video - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'avi' => 'video/x-msvideo', - 'mpg' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'm1v' => 'video/mpeg', - 'm2v' => 'video/mpeg', - '3gp' => 'video/3gpp', - '3g2' => 'video/3gpp2', - 'h261' => 'video/h261', - 'h263' => 'video/h263', - 'h264' => 'video/h264', - 'jpgv' => 'video/jpgv', - 'mp4' => 'video/mp4', - 'mp4v' => 'video/mp4', - 'mpg4' => 'video/mp4', - 'ogv' => 'video/ogg', - 'webm' => 'video/webm', - 'm4v' => 'video/x-m4v', - 'asf' => 'video/x-ms-asf', - 'asx' => 'video/x-ms-asf', - 'wm' => 'video/x-ms-wm', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wvx' => 'video/x-ms-wvx', - 'movie' => 'video/x-sgi-movie', - - // adobe - 'pdf' => 'application/pdf', - 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - - // ms office - 'doc' => 'application/msword', - 'dot' => 'application/msword', - 'rtf' => 'application/rtf', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - - // open office - 'odt' => 'application/vnd.oasis.opendocument.text', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odp' => 'application/vnd.oasis.opendocument.presentation' - - ); - - $sExt = static::GetFileExtension($sFileName); - if (\strlen($sExt) && isset($aMimeTypes[$sExt])) - { - $sResult = $aMimeTypes[$sExt]; - } - - return $sResult; - } - public static function ContentTypeType(string $sContentType, string $sFileName) : string { $sContentType = \strtolower($sContentType); @@ -695,6 +521,7 @@ abstract class Utils case 'application/x-bzip2': case 'application/x-debian-package': case 'application/x-tar': + case 'application/gtar': return 'archive'; case 'application/msword': diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php index a81493c41..10ef808da 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Attachment.php @@ -22,46 +22,28 @@ class Attachment */ private $rResource; - /** - * @var string - */ - private $sFileName; + private string $sFileName; - /** - * @var int - */ - private $iFileSize; + private int $iFileSize; - /** - * @var string - */ - private $sCID; + private string $sCID; - /** - * @var bool - */ - private $bIsInline; + private bool $bIsInline; - /** - * @var bool - */ - private $bIsLinked; + private bool $bIsLinked; - /** - * @var array - */ - private $aCustomContentTypeParams; + private array $aCustomContentTypeParams; - /** - * @var string - */ - private $sContentLocation; + private string $sContentLocation; + + private string $sContentType; /** * @param resource $rResource */ function __construct($rResource, string $sFileName, int $iFileSize, bool $bIsInline, - bool $bIsLinked, string $sCID, array $aCustomContentTypeParams = [], string $sContentLocation = '') + bool $bIsLinked, string $sCID, array $aCustomContentTypeParams = [], + string $sContentLocation = '', string $sContentType = '') { $this->rResource = $rResource; $this->sFileName = $sFileName; @@ -71,6 +53,10 @@ class Attachment $this->sCID = $sCID; $this->aCustomContentTypeParams = $aCustomContentTypeParams; $this->sContentLocation = $sContentLocation; + $this->sContentType = $sContentType + ?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName) + ?: \SnappyMail\File\MimeType::fromFilename($sFileName) + ?: 'application/octet-stream'; } /** @@ -83,7 +69,7 @@ class Attachment public function ContentType() : string { - return \MailSo\Base\Utils::MimeContentType($this->sFileName); + return $this->sContentType; } public function CustomContentTypeParams() : array diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 32faf6442..257e99e3d 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -997,18 +997,23 @@ class Actions if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) { $sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']); + + // Detect content-type + $type = \SnappyMail\File\MimeType::fromFile($aFile['tmp_name'], $aFile['name']) + ?: \SnappyMail\File\MimeType::fromFilename($aFile['name']); + if ($type) { + $aFile['type'] = $type; + $sSavedName .= \SnappyMail\File\MimeType::toExtension($type); + } + if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name'])) { $iError = Enumerations\UploadError::ON_SAVING; } else { - $sUploadName = $aFile['name']; - $iSize = $aFile['size']; - $sMimeType = $aFile['type']; - $aResponse['Attachment'] = array( - 'Name' => $sUploadName, + 'Name' => $aFile['name'], 'TempName' => $sSavedName, - 'MimeType' => $sMimeType, - 'Size' => (int)$iSize + 'MimeType' => $aFile['type'], + 'Size' => (int) $aFile['size'] ); } } @@ -1041,9 +1046,12 @@ class Actions $iError = $this->GetActionParam('Error', Enumerations\UploadError::UNKNOWN); if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) { - $sMimeType = \strtolower(\MailSo\Base\Utils::MimeContentType($aFile['name'])); - if (\in_array($sMimeType, array('image/png', 'image/jpg', 'image/jpeg'))) { - $sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']); + $sMimeType = \SnappyMail\File\MimeType::fromFile($aFile['tmp_name'], $aFile['name']) + ?: \SnappyMail\File\MimeType::fromFilename($aFile['name']) + ?: $aFile['type']; + if (\in_array($sMimeType, array('image/png', 'image/jpg', 'image/jpeg', 'image/webp'))) { + $sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']) + . \SnappyMail\File\MimeType::toExtension($sContentType); if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name'])) { $iError = Enumerations\UploadError::ON_SAVING; } else { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index 507ef448d..31ec11f31 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -603,37 +603,53 @@ trait Messages try { $aAttachments = $this->GetActionParam('Attachments', array()); - if (\is_array($aAttachments) && \count($aAttachments)) - { - $mResult = array(); - foreach ($aAttachments as $sAttachment) - { - if ($aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment)) - { + if (!\is_array($aAttachments)) { + $aAttachments = []; + } + if (\count($aAttachments)) { + $oFilesProvider = $this->FilesProvider(); + foreach ($aAttachments as $mIndex => $sAttachment) { + $aAttachments[$mIndex] = false; + if ($aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment)) { $sFolder = isset($aValues['Folder']) ? (string) $aValues['Folder'] : ''; $iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0; $sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : ''; - $sTempName = \md5($sAttachment); - if (!$this->FilesProvider()->FileExists($oAccount, $sTempName)) - { + $sTempName = \sha1($sAttachment); + if (!$oFilesProvider->FileExists($oAccount, $sTempName)) { $this->MailClient()->MessageMimeStream( - function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, &$mResult, $sTempName, $sAttachment, $self) { - if (is_resource($rResource)) - { - $sContentType = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName); - $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); + function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, $sTempName, $self, &$aAttachments, $mIndex) { + if (\is_resource($rResource)) { + $sContentType = + $sContentType + ?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName) + ?: \SnappyMail\File\MimeType::fromFilename($sFileName) + ?: 'application/octet-stream'; // 'text/plain' - if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) - { - $mResult[$sTempName] = $sAttachment; +// $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); + $sTempName .= \SnappyMail\File\MimeType::toExtension($sContentType); + + if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) { + $aAttachments[$mIndex] = [ +// 'Name' => $sFileName, + 'TempName' => $sTempName, + 'MimeType' => $sContentType +// 'Size' => 0 + ]; } } }, $sFolder, $iUid, $sMimeIndex); - } - else - { - $mResult[$sTempName] = $sAttachment; + } else { + $rResource = $oFilesProvider->GetFile($oAccount, $sTempName); + $sContentType = \SnappyMail\File\MimeType::fromStream($rResource, $sTempName) + ?: \SnappyMail\File\MimeType::fromFilename($sTempName) + ?: 'application/octet-stream'; // 'text/plain' + $aAttachments[$mIndex] = [ +// 'Name' => $sFileName, + 'TempName' => $sTempName, + 'MimeType' => $sContentType +// 'Size' => $oFilesProvider->FileSize($oAccount, $sTempName) + ]; } } } @@ -644,7 +660,7 @@ trait Messages throw new ClientException(Notifications::MailServerError, $oException); } - return $this->DefaultResponse(__FUNCTION__, $mResult); + return $this->DefaultResponse(__FUNCTION__, $aAttachments); } /** @@ -1231,10 +1247,11 @@ trait Messages { foreach ($aAttachments as $sTempName => $aData) { - $sFileName = (string) $aData[0]; - $bIsInline = (bool) $aData[1]; - $sCID = (string) $aData[2]; - $sContentLocation = isset($aData[3]) ? (string) $aData[3] : ''; + $sFileName = (string) $aData['name']; + $bIsInline = (bool) $aData['inline']; + $sCID = (string) $aData['cid']; + $sContentLocation = (string) $aData['location']; + $sMimeType = (string) $aData['type']; $rResource = $this->FilesProvider()->GetFile($oAccount, $sTempName); if (\is_resource($rResource)) @@ -1244,7 +1261,7 @@ trait Messages $oMessage->Attachments()->append( new \MailSo\Mime\Attachment($rResource, $sFileName, $iFileSize, $bIsInline, \in_array(trim(trim($sCID), '<>'), $aFoundCids), - $sCID, array(), $sContentLocation + $sCID, array(), $sContentLocation, $sMimeType ) ); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php index d639eea6c..0e9c78f42 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php @@ -149,7 +149,9 @@ trait Raw if ('.pdf' === \substr($sFileNameIn,-4)) { $sContentTypeOut = 'application/pdf'; // application/octet-stream } else { - $sContentTypeOut = $sContentTypeIn ?: \MailSo\Base\Utils::MimeContentType($sFileNameIn); + $sContentTypeOut = $sContentTypeIn + ?: \SnappyMail\File\MimeType::fromFilename($sFileNameIn) + ?: 'application/octet-stream'; } $sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex); @@ -197,10 +199,12 @@ trait Raw if ('.pdf' === \substr($sFileName, -4)) { // https://github.com/the-djmaze/snappymail/issues/144 $sContentType = 'application/pdf'; - } else if ($sContentTypeIn) { - $sContentType = $sContentTypeIn; - } else if (!$sContentType) { - $sContentType = $sFileName ? \MailSo\Base\Utils::MimeContentType($sFileName) : 'text/plain'; + } else { + $sContentType = $sContentTypeIn + ?: $sContentType +// ?: \SnappyMail\File\MimeType::fromStream($rResource, $sFileName) + ?: \SnappyMail\File\MimeType::fromFilename($sFileName) + ?: 'application/octet-stream'; } if (!$bDownload) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/file/magic.mime.php b/snappymail/v/0.0.0/app/libraries/snappymail/file/magic.mime.php new file mode 100644 index 000000000..2b79d0069 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/file/magic.mime.php @@ -0,0 +1,34 @@ + 'application/x-7z-compressed', # unknown by magic.mime + '#^BZh.*#s' => 'application/x-bzip2', + '#^\x1f\x8b.*#s' => 'application/x-gzip', + '#^.{257}ustar \x00.*#s' => 'application/x-gtar', + '#^.{257}ustar\x00.*#s' => 'application/x-tar', + '#^Rar!.*#s' => 'application/x-rar-compressed', + '#^PK\x03\x04.*#s' => 'application/zip', + + '#^%PDF-.*#s' => 'application/pdf', + '#^[CF]WS.*#s' => 'application/x-shockwave-flash', + + '#^(.*\n)?(INSERT|CREATE|DROP|DELETE|ALTER|UPDATE)\ .*#is' => 'text/x-sql', + '#^BEGIN:VCARD#s' => 'text/x-vcard', + + '#^GIF8.*#s' => 'image/gif', + '#^.{8}heic#s' => 'image/heic', // https://nokiatech.github.io/heif/technical.html + '#^\xFF\xD8.*#s' => 'image/jpeg', + '#^\x89PNG.*#s' => 'image/png', + '#^.PNG.*#s' => 'image/png', + '#^ 'image/svg+xml', # wild guess, unknown by magic.mime + '#^RIFF.{4}WEBP#s' => 'image/webp', + + '#^FLV.*#s' => 'video/x-flv', + '#^OggS.+\x80theora.*#s' => 'video/ogg', + + '#^ID3.*#s' => 'audio/mpeg', # wild guess? + '#^\xff\xfa.*#s' => 'audio/mpeg', # wild guess? + '#^OggS.+\x01vorbis.*#s' => 'audio/ogg', + + '#^\xD0\xCF\x11\xE0\xA1.*#s' => 'application/msword', + '#^OggS.*#s' => 'application/ogg', +); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php b/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php new file mode 100644 index 000000000..ba44e9cd6 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/file/mimetype.php @@ -0,0 +1,276 @@ +file($filename)); + } + if (!$mime && $fp = \fopen($filename, 'rb')) { + $mime = self::fromStream($fp); + \fclose($fp); + } + if ('application/zip' === \str_replace('/x-', '/', $mime)) { + $zip = new \ZipArchive($filename); + if (false !== $zip->locateName('word/_rels/document.xml.rels')) { + return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; + } + if (false !== $zip->locateName('xl/_rels/workbook.xml.rels')) { + return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; + } + } + } + return $mime ? static::detectDeeper($mime, $name ?: $filename) : null; + } + + public static function fromStream($stream, string $name = '') : ?string + { + if (\is_resource($stream) && \stream_get_meta_data($stream)['seekable']) { + $pos = \ftell($stream); +// if (\is_int($pos) && \rewind($stream)) { + if (\is_int($pos) && 0 === \fseek($stream, 0)) { +// $str = \fread($stream, 265); + $str = \stream_get_contents($stream, 265, 0); + \fseek($stream, $pos); + if ($str) { + return static::fromString($str, $name); + } + } + } + return null; + } + + public static function fromString(string &$str, string $name = '') : ?string + { + static::initFInfo(); + $mime = self::$finfo + ? \preg_replace('#[,;].*#', '', self::$finfo->buffer($str)) + : self::getFromData($str); + return $mime ? static::detectDeeper($mime, $name) : null; + } + + protected static function getFromData(string $str) : ?string + { + if (\str_contains($str, '-----BEGIN PGP SIGNATURE-----')) { + return 'application/pgp-signature'; + } + if (\preg_match('/-----BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK-----/', $str)) { + return 'application/pgp-keys'; + } + static $magic; + if (!$magic) { + require __DIR__ . '/magic.mime.php'; + } + $str = \preg_replace(\array_keys($magic), \array_values($magic), $str, 1, $c); + return $c ? $str : null; + } + + public static function fromFilename(string $filename) : ?string + { + $filename = \strtolower($filename); + if ('winmail.dat' === $filename) { + return 'application/ms-tnef'; + } + $extension = \explode('.', $filename); + $extension = \array_pop($extension); + return isset(static::$types[$extension]) ? static::$types[$extension] : null; + } + + /** + * Issue with 'text/plain' + */ + public static function toExtension(string $mime, bool $include_dot = true) : ?string + { + $mime = \strtolower($mime); + if ('application/pgp-signature' == $mime || 'application/pgp-keys' == $mime) { + $ext = 'asc'; + } else { + $mime = \str_replace('application/x-tar', 'application/gtar', $mime); + $ext = \array_search($mime, static::$types) + ?: \array_search(\str_replace('/x-', '/', $mime), static::$types) + ?: \array_search(\str_replace('/', '/x-', $mime), static::$types) + ?: 'bin'; + } + return ($include_dot ? '.' : '') . $ext; + } + + protected static $types = [ + '7z' => 'application/x-7z-compressed', + 'ai' => 'application/postscript', +// 'asc' => 'application/pgp-signature', +// 'asc' => 'application/pgp-keys', + 'bat' => 'application/x-msdownload', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'cab' => 'application/vnd.ms-cab-compressed', + 'chm' => 'application/vnd.ms-htmlhelp', + 'com' => 'application/x-msdownload', + 'deb' => 'application/x-debian-package', + 'dll' => 'application/x-msdownload', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'eps' => 'application/postscript', + 'epub' => 'application/epub', + 'exe' => 'application/x-msdownload', + 'gz' => 'application/gzip', + 'gz' => 'application/x-gzip', + 'hlp' => 'application/winhlp', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'msi' => 'application/x-msdownload', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ogx' => 'application/ogg', + 'p10' => 'application/pkcs10', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7s' => 'application/pkcs7-signature', + 'pdf' => 'application/pdf', + 'php' => 'application/x-httpd-php', + 'php' => 'application/x-php', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'psd' => 'image/vnd.adobe.photoshop', + 'rar' => 'application/rar-compressed', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'scr' => 'application/x-msdownload', + 'sql' => 'application/sql', + 'swf' => 'application/shockwave-flash', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/gtar', +// 'tar' => 'application/x-tar', +// 'tgz' => 'application/x-gzip', + 'torrent' => 'application/x-bittorrent', + 'wgt' => 'application/widget', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'zip' => 'application/zip', + + 'aac' => 'audio/aac', + 'aif' => 'audio/aiff', + 'aifc' => 'audio/aiff', + 'aiff' => 'audio/aiff', + 'flac' => 'audio/flac', + 'm3u' => 'audio/x-mpegurl', + 'midi' => 'audio/midi', + 'mp3' => 'audio/mpeg', + 'mp4a' => 'audio/mp4', + 'ogg' => 'audio/ogg', + 'wav' => 'audio/wav', + 'weba' => 'audio/webm', + + 'ttf' => 'font/ttf', + 'woff' => 'font/woff', + 'woff2' => 'font/woff2', + + 'bmp' => 'image/bmp', + 'cgm' => 'image/cgm', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'gif' => 'image/gif', +// 'heic' => 'image/heic', + 'ico' => 'image/vnd.microsoft.icon', +// 'ico' => 'image/x-icon', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jfif' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'webp' => 'image/webp', + + 'eml' => 'message/rfc822', + 'mime' => 'message/rfc822', + + 'txt' => 'text/plain', + 'asp' => 'text/asp', + 'cfg' => 'text/plain', + 'conf' => 'text/plain', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'def' => 'text/plain', + 'html' => 'text/html', + 'htm' => 'text/html', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'list' => 'text/plain', + 'log' => 'text/plain', + 'pl' => 'text/perl', + 'rtx' => 'text/richtext', + 'text' => 'text/plain', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'xml' => 'text/xml', + + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'avi' => 'video/x-msvideo', + 'flv' => 'video/flv', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'jpgv' => 'video/jpgv', + 'm4v' => 'video/x-m4v', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp4' => 'video/mp4', + 'mp4v' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'm1v' => 'video/mpeg', + 'm2v' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'ogv' => 'video/ogg', + 'qt' => 'video/quicktime', + 'webm' => 'video/webm', + 'wm' => 'video/x-ms-wm', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wvx' => 'video/x-ms-wvx', + ]; + +} From 481ef709768cbed0032fa5add8c20e3615babfe1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 15:33:53 +0100 Subject: [PATCH 046/114] Resolve #677 --- .../v/0.0.0/app/templates/Views/Admin/AdminSettingsAbout.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsAbout.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsAbout.html index 681b7c59e..ebf340ec9 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsAbout.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsAbout.html @@ -34,8 +34,8 @@
    - - + +
    From 10e36e96b62e53efad2dc44a388f208bddf6ddc7 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 15:34:40 +0100 Subject: [PATCH 047/114] Show to admin if PHP Phar and Fileinfo are available --- .../v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php | 8 ++++++++ snappymail/v/0.0.0/setup.php | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php index 9908e4565..5d8517b79 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php @@ -410,6 +410,14 @@ class ActionsAdmin extends Actions 'loaded' => \extension_loaded(\strtolower($name)) ]; } + $aResult[] = [ + 'name' => 'Fileinfo', + 'loaded' => \class_exists('finfo') + ]; + $aResult[] = [ + 'name' => 'Phar', + 'loaded' => \class_exists('PharData') + ]; return $this->DefaultResponse(__FUNCTION__, $aResult); } diff --git a/snappymail/v/0.0.0/setup.php b/snappymail/v/0.0.0/setup.php index 89e52397d..bc9ccce77 100644 --- a/snappymail/v/0.0.0/setup.php +++ b/snappymail/v/0.0.0/setup.php @@ -29,7 +29,8 @@ if (defined('APP_VERSION')) 'tidy' => extension_loaded('tidy'), 'uuid' => extension_loaded('uuid'), 'xxtea' => extension_loaded('xxtea'), - 'zip' => extension_loaded('zip') + 'zip' => extension_loaded('zip'), + 'finfo' => class_exists('finfo') ); $aRequirements = array( From ff81aa6abc2bb2382a394c84bc143d11bbe197df Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 15:48:02 +0100 Subject: [PATCH 048/114] Better solution for #501 --- dev/External/SquireUI.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/External/SquireUI.js b/dev/External/SquireUI.js index 46b7e08d6..e7f6e5343 100644 --- a/dev/External/SquireUI.js +++ b/dev/External/SquireUI.js @@ -493,7 +493,12 @@ class SquireUI } focus() { - ('plain' == this.mode ? this.plain : this.squire).focus(); + if ('plain' == this.mode) { + this.plain.focus(); + this.plain.setSelectionRange(0, 0); + } else { + this.squire.focus(); + } } } From e5ef0eaa6a6acb82d4a255b96dcafddcccb4b18d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 15:55:16 +0100 Subject: [PATCH 049/114] Resolve #673 --- dev/Knoin/Knoin.js | 1 + integrations/nextcloud/snappymail/css/embed.css | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index 8636c7166..b69bfeab1 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -51,6 +51,7 @@ const // Firefox < 98 / Safari < 15.4 HTMLDialogElement not defined if (!vmDom.showModal) { + vmDom.className = 'polyfill'; vmDom.showModal = () => { vmDom.backdrop || vmDom.before(vmDom.backdrop = Element.fromHTML('
    ')); diff --git a/integrations/nextcloud/snappymail/css/embed.css b/integrations/nextcloud/snappymail/css/embed.css index 0df67cffa..130035ffb 100644 --- a/integrations/nextcloud/snappymail/css/embed.css +++ b/integrations/nextcloud/snappymail/css/embed.css @@ -105,6 +105,15 @@ body > header ul { cursor: pointer; } +/** + * Firefox < 98 + * https://github.com/the-djmaze/snappymail/issues/673 + */ +#rl-app dialog.polyfill { + margin-top: 50px; + max-height: calc(100vh - 60px); +} + /** * hampoelz * https://github.com/the-djmaze/snappymail/issues/96#issuecomment-1279783076 From 92240e84a68a090d44a046a7edd0f918a65262ce Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 16:20:10 +0100 Subject: [PATCH 050/114] Removed \MailSo\Mime\Enumerations\MimeType --- .../MailSo/Mime/Enumerations/MimeType.php | 41 ---------- .../app/libraries/MailSo/Mime/Message.php | 15 +--- .../libraries/RainLoop/Actions/Messages.php | 74 +++++++++---------- 3 files changed, 39 insertions(+), 91 deletions(-) delete mode 100644 snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MimeType.php diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MimeType.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MimeType.php deleted file mode 100644 index 625b587b6..000000000 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Enumerations/MimeType.php +++ /dev/null @@ -1,41 +0,0 @@ -IsLinked()) { $oRelatedPart = new Part; $oRelatedPart->Headers->append( - new Header( - Enumerations\Header::CONTENT_TYPE, - Enumerations\MimeType::MULTIPART_RELATED - ) + new Header(Enumerations\Header::CONTENT_TYPE, 'multipart/related') ); $oRelatedPart->SubParts->append($oRootPart); $oRootPart = $oRelatedPart; @@ -399,10 +396,7 @@ class Message extends Part } } else { $oRootPart = new Part; - $oRootPart->Headers->AddByName( - Enumerations\Header::CONTENT_TYPE, - Enumerations\MimeType::MULTIPART_MIXED - ); + $oRootPart->Headers->AddByName(Enumerations\Header::CONTENT_TYPE, 'multipart/mixed'); $oRootPart->SubParts = $this->SubParts; } @@ -413,10 +407,7 @@ class Message extends Part } else { if (!$oMixedPart) { $oMixedPart = new Part; - $oMixedPart->Headers->AddByName( - Enumerations\Header::CONTENT_TYPE, - Enumerations\MimeType::MULTIPART_MIXED - ); + $oMixedPart->Headers->AddByName(Enumerations\Header::CONTENT_TYPE, 'multipart/mixed'); $oMixedPart->SubParts->append($oRootPart); $oRootPart = $oMixedPart; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index 31ec11f31..25ca344b7 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -10,6 +10,7 @@ use MailSo\Imap\SequenceSet; use MailSo\Imap\Enumerations\FetchType; use MailSo\Imap\Enumerations\MessageFlag; use MailSo\Mime\Part as MimePart; +use MailSo\Mime\Enumerations\Header as MimeEnumHeader; trait Messages { @@ -715,7 +716,7 @@ trait Messages 'text' => $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$sBodyPartId.']'), 'signature' => '' ]; - $decode = (new \MailSo\Mime\HeaderCollection($sBodyMime))->ValueByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING); + $decode = (new \MailSo\Mime\HeaderCollection($sBodyMime))->ValueByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING); if ('base64' === $decode) { $result['text'] = \base64_decode($result['text']); } else if ('quoted-printable' === $decode) { @@ -837,8 +838,8 @@ trait Messages if ($this->Config()->Get('labs', 'mail_func_clear_headers', true)) { $sMailHeaders = \MailSo\Base\Utils::RemoveHeaderFromHeaders($sMailHeaders, array( - \MailSo\Mime\Enumerations\Header::TO_, - \MailSo\Mime\Enumerations\Header::SUBJECT + MimeEnumHeader::TO_, + MimeEnumHeader::SUBJECT )); } @@ -1027,8 +1028,8 @@ trait Messages $this->Plugins()->RunHook('filter.read-receipt-message-plain', array($oAccount, $oMessage, &$sText)); $oPart = new MimePart; - $oPart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"'); - $oPart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); + $oPart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'text/plain; charset="utf-8"'); + $oPart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); $oPart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream( \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sText))), 'convert.quoted-printable-encode' @@ -1130,7 +1131,7 @@ trait Messages $oPart = new MimePart; $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, + MimeEnumHeader::CONTENT_TYPE, 'multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"; boundary="'.$sBoundary.'"' ); $oPart->Body = $aSigned[1]; @@ -1143,22 +1144,22 @@ trait Messages } else if ($sEncrypted = $this->GetActionParam('Encrypted', '')) { $oPart = new MimePart; $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, + MimeEnumHeader::CONTENT_TYPE, 'multipart/encrypted; protocol="application/pgp-encrypted"' ); $oMessage->SubParts->append($oPart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-encrypted'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'attachment'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/pgp-encrypted'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_DISPOSITION, 'attachment'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString('Version: 1'); $oPart->SubParts->append($oAlternativePart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/octet-stream'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'inline; filename="msg.asc"'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/octet-stream'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_DISPOSITION, 'inline; filename="msg.asc"'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sEncrypted))); $oPart->SubParts->append($oAlternativePart); @@ -1168,10 +1169,7 @@ trait Messages } else { if ($sHtml = $this->GetActionParam('Html', '')) { $oPart = new MimePart; - $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, - \MailSo\Mime\Enumerations\MimeType::MULTIPART_ALTERNATIVE - ); + $oPart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'multipart/alternative'); $oMessage->SubParts->append($oPart); $sHtml = \MailSo\Base\HtmlUtils::BuildHtml($sHtml, $aFoundCids, $aFoundDataURL, $aFoundContentLocationUrls); @@ -1181,8 +1179,8 @@ trait Messages $sPlain = $this->GetActionParam('Text', '') ?: \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sHtml); $this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain)); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset=utf-8'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'text/plain; charset=utf-8'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); $oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream( \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sPlain))), 'convert.quoted-printable-encode' @@ -1192,8 +1190,8 @@ trait Messages // Now add HTML $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/html; charset=utf-8'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'text/html; charset=utf-8'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); $oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream( \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sHtml))), 'convert.quoted-printable-encode' @@ -1208,27 +1206,27 @@ trait Messages if ($sSignature = $this->GetActionParam('Signature', null)) { $oPart = new MimePart; $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, + MimeEnumHeader::CONTENT_TYPE, 'multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"' ); $oMessage->SubParts->append($oPart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"; protected-headers="v1"'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'base64'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'text/plain; charset="utf-8"; protected-headers="v1"'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, 'base64'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sPlain))); $oPart->SubParts->append($oAlternativePart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-signature; name="signature.asc"'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/pgp-signature; name="signature.asc"'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sSignature))); $oPart->SubParts->append($oAlternativePart); } else { $this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain)); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'text/plain; charset="utf-8"'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'text/plain; charset="utf-8"'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, 'quoted-printable'); $oAlternativePart->Body = \MailSo\Base\StreamWrappers\Binary::CreateStream( \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString(\preg_replace('/\\r?\\n/su', "\r\n", \trim($sPlain))), 'convert.quoted-printable-encode' @@ -1319,7 +1317,7 @@ trait Messages $oPart = new MimePart; $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, + MimeEnumHeader::CONTENT_TYPE, 'multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"' ); $oMessage->SubParts->append($oPart); @@ -1329,8 +1327,8 @@ trait Messages $oPart->SubParts->append($oBody); $oSignaturePart = new MimePart; - $oSignaturePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-signature; name="signature.asc"'); - $oSignaturePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oSignaturePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/pgp-signature; name="signature.asc"'); + $oSignaturePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oSignaturePart->Body = $sSignature; $oPart->SubParts->append($oSignaturePart); } @@ -1354,22 +1352,22 @@ trait Messages $oPart = new MimePart; $oPart->Headers->AddByName( - \MailSo\Mime\Enumerations\Header::CONTENT_TYPE, + MimeEnumHeader::CONTENT_TYPE, 'multipart/encrypted; protocol="application/pgp-encrypted"' ); $oMessage->SubParts->append($oPart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/pgp-encrypted'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'attachment'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/pgp-encrypted'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_DISPOSITION, 'attachment'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString('Version: 1'); $oPart->SubParts->append($oAlternativePart); $oAlternativePart = new MimePart; - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'application/octet-stream'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION, 'inline; filename="msg.asc"'); - $oAlternativePart->Headers->AddByName(\MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING, '7Bit'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'application/octet-stream'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_DISPOSITION, 'inline; filename="msg.asc"'); + $oAlternativePart->Headers->AddByName(MimeEnumHeader::CONTENT_TRANSFER_ENCODING, '7Bit'); $oAlternativePart->Body = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString($sEncrypted); $oPart->SubParts->append($oAlternativePart); } From 48f4e51dc5a3de00ec3740d60de93689382be9e2 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 16 Nov 2022 17:05:53 +0100 Subject: [PATCH 051/114] Properly handle GnuPG decrypt failure #600 --- dev/Stores/User/GnuPG.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/Stores/User/GnuPG.js b/dev/Stores/User/GnuPG.js index f42053b2f..8a3657baa 100644 --- a/dev/Stores/User/GnuPG.js +++ b/dev/Stores/User/GnuPG.js @@ -186,7 +186,7 @@ export const GnuPGUserStore = new class { } if (null !== params.Passphrase) { const result = await Remote.post('GnupgDecrypt', null, params); - if (result?.Result) { + if (result?.Result && false !== result.Result.data) { return result.Result; } } From 28a70ccc8e9b46c6f24f5ff8e37a2b11b8d03157 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 10:47:07 +0100 Subject: [PATCH 052/114] Resolve #679 --- .../Providers/AddressBook/Classes/Contact.php | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 316764420..9b8fd15bf 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -2,43 +2,26 @@ namespace RainLoop\Providers\AddressBook\Classes; +use \Sabre\VObject\Component\VCard; + class Contact implements \JsonSerializable { - /** - * @var string - */ - public $id = ''; + public string $id = ''; + + public string $IdContactStr = ''; + +// public string $Display; + + public int $Changed; + + public bool $ReadOnly = false; /** - * @var string - */ - public $IdContactStr = ''; - - /** - * @var string - */ -// public $Display; - - /** - * @var int - */ - public $Changed; - - /** - * @var bool - */ - public $ReadOnly = false; - - /** - * @var string * Used for CardDAV synchronization */ - public $Etag = ''; + public string $Etag = ''; - /** - * @var \Sabre\VObject\Component\VCard - */ - protected $vCard = null; + protected ?VCard $vCard = null; function __construct() { @@ -62,8 +45,15 @@ class Contact implements \JsonSerializable return \array_unique($aResult); } */ - public function setVCard(\Sabre\VObject\Component\VCard $oVCard) : void + public function setVCard(VCard $oVCard) : void { + if ($oVCard->PHOTO && $oVCard->PHOTO->parameters['ENCODING']) { + $oVCard->VERSION = '3.0'; + } + if (VCard::VCARD40 != $oVCard->getDocumentType()) { + $oVCard = $oVCard->convert(VCard::VCARD40); + } + // KDE KAddressBook entry and used by SnappyMail // https://github.com/sabre-io/vobject/issues/589 $oVCard->select('X-CRYPTO') From 28a7377f976d465ac78ae55427fb7d925aac3ffa Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 11:27:29 +0100 Subject: [PATCH 053/114] Bugfix: DAV getContactsPaths() failed due to failures of `(string) SimpleXMLElement` Added better logging of DAV connection for #653 and #680 --- .../libraries/RainLoop/Actions/Contacts.php | 4 +- .../Providers/AddressBook/CardDAV.php | 157 ++++++++---------- .../app/libraries/snappymail/dav/client.php | 28 ++-- 3 files changed, 87 insertions(+), 102 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php index 992433a44..d419251f2 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php @@ -35,13 +35,13 @@ trait Contacts $oAccount = $this->getAccountFromToken(); $oAddressBookProvider = $this->AddressBookProvider($oAccount); if (!$oAddressBookProvider) { - throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError); + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'No AddressBookProvider'); } \ignore_user_abort(true); \SnappyMail\HTTP\Stream::start(/*$binary = false*/); \SnappyMail\HTTP\Stream::JSON(['messsage'=>'start']); if (!$oAddressBookProvider->Sync()) { - throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError); + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'AddressBookProvider->Sync() failed'); } return $this->TrueResponse(__FUNCTION__); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/CardDAV.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/CardDAV.php index 453e02026..1ca58b5c1 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/CardDAV.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/CardDAV.php @@ -28,15 +28,11 @@ trait CardDAV $aResponse = null; try { - $this->oLogger->Write('PROPFIND '.$sPath, \LOG_INFO, 'DAV'); - $aResponse = $oClient->propFind($sPath, array( '{DAV:}getlastmodified', '{DAV:}resourcetype', '{DAV:}getetag' ), 1); - -// $this->oLogger->WriteDump($aResponse); } catch (\Throwable $oException) { @@ -139,16 +135,12 @@ trait CardDAV try { - $this->oLogger->Write('PROPFIND '.$sPath, \LOG_INFO, 'DAV'); - $aResponse = $oClient->propFind($sPath, array( '{DAV:}current-user-principal', '{DAV:}resourcetype', '{DAV:}displayname', '{urn:ietf:params:xml:ns:carddav}addressbook-home-set' ), 1); - -// $this->oLogger->WriteDump($aResponse); } catch (\Throwable $oException) { @@ -158,49 +150,47 @@ trait CardDAV return $aResponse; } - private function getContactsPaths(DAVClient $oClient, string $sUser, string $sPassword, string $sProxy = '') : array + private function getContactsPaths(DAVClient $oClient, string $sPath, string $sUser, string $sPassword, string $sProxy = '') : array { $aContactsPaths = array(); $sCurrentUserPrincipal = ''; $sAddressbookHomeSet = ''; -// [{DAV:}current-user-principal] => /cloud/remote.php/carddav/principals/admin/ -// [{urn:ietf:params:xml:ns:carddav}addressbook-home-set] => /cloud/remote.php/carddav/addressbooks/admin/ - - $aResponse = $this->detectionPropFind($oClient, '/.well-known/carddav'); + $aResponse = $this->detectionPropFind($oClient, '/.well-known/carddav') + ?: $this->detectionPropFind($oClient, $sPath); $sNextPath = ''; $sFirstNextPath = ''; if (\is_array($aResponse)) { - foreach ($aResponse as $sKey => $aItem) + foreach ($aResponse as $sPropPath => $aItem) { - if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) && - false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy')) + if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']) + && false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href'], '/calendar-proxy')) { - $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']; + $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']; continue; } - if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal'])) + if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) { - $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']; + $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']['{DAV:}href']; continue; } - if (!empty($sKey)) + if (!empty($sPropPath)) { if (empty($sFirstNextPath)) { - $sFirstNextPath = $sKey; + $sFirstNextPath = $sPropPath; } if (empty($sNextPath)) { if (static::hasDAVCollection($aItem)) { - $sNextPath = $sKey; + $sNextPath = $sPropPath; continue; } } @@ -219,36 +209,32 @@ trait CardDAV { return $aContactsPaths; } - else + if (\preg_match('/^http[s]?:\/\//i', $sNextPath)) { - if (\preg_match('/^http[s]?:\/\//i', $sNextPath)) + $oClient = $this->getDavClientFromUrl($sNextPath, $sUser, $sPassword, $sProxy); + if (!$oClient) { - $oClient = $this->getDavClientFromUrl($sNextPath, $sUser, $sPassword, $sProxy); - if ($oClient) - { - $sNextPath = $oClient->__UrlPath__; - } - else - { - return $aContactsPaths; - } + return $aContactsPaths; } + $sNextPath = $oClient->__UrlPath__; + } + if ($sPath != $sNextPath) { $aResponse = $this->detectionPropFind($oClient, $sNextPath); if (\is_array($aResponse)) { - foreach ($aResponse as $sKey => $aItem) + foreach ($aResponse as $aItem) { - if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) && - false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy')) + if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']) && + false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href'], '/calendar-proxy')) { - $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']; + $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']; continue; } - if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal'])) + if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) { - $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']; + $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']['{DAV:}href']; continue; } } @@ -262,32 +248,29 @@ trait CardDAV { return $aContactsPaths; } - else + if (\preg_match('/^http[s]?:\/\//i', $sCurrentUserPrincipal)) { - if (\preg_match('/^http[s]?:\/\//i', $sCurrentUserPrincipal)) + $oClient = $this->getDavClientFromUrl($sCurrentUserPrincipal, $sUser, $sPassword, $sProxy); + if ($oClient) { - $oClient = $this->getDavClientFromUrl($sCurrentUserPrincipal, $sUser, $sPassword, $sProxy); - if ($oClient) - { - $sCurrentUserPrincipal = $oClient->__UrlPath__; - } - else - { - return $aContactsPaths; - } + $sCurrentUserPrincipal = $oClient->__UrlPath__; } - - $aResponse = $this->detectionPropFind($oClient, $sCurrentUserPrincipal); - if (\is_array($aResponse)) + else { - foreach ($aResponse as $sKey => $aItem) + return $aContactsPaths; + } + } + + $aResponse = $this->detectionPropFind($oClient, $sCurrentUserPrincipal); + if (\is_array($aResponse)) + { + foreach ($aResponse as $aItem) + { + if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']) && + false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href'], '/calendar-proxy')) { - if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) && - false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy')) - { - $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']; - continue; - } + $sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']['{DAV:}href']; + continue; } } } @@ -297,31 +280,28 @@ trait CardDAV { return $aContactsPaths; } - else + if (\preg_match('/^http[s]?:\/\//i', $sAddressbookHomeSet)) { - if (\preg_match('/^http[s]?:\/\//i', $sAddressbookHomeSet)) + $oClient = $this->getDavClientFromUrl($sAddressbookHomeSet, $sUser, $sPassword, $sProxy); + if ($oClient) { - $oClient = $this->getDavClientFromUrl($sAddressbookHomeSet, $sUser, $sPassword, $sProxy); - if ($oClient) - { - $sAddressbookHomeSet = $oClient->__UrlPath__; - } - else - { - return $aContactsPaths; - } + $sAddressbookHomeSet = $oClient->__UrlPath__; } - - $aResponse = $this->detectionPropFind($oClient, $sAddressbookHomeSet); - if (\is_array($aResponse)) + else { - foreach ($aResponse as $sKey => $aItem) + return $aContactsPaths; + } + } + + $aResponse = $this->detectionPropFind($oClient, $sAddressbookHomeSet); + if (\is_array($aResponse)) + { + foreach ($aResponse as $sPropPath => $aItem) + { + if (!empty($sPropPath) && static::hasDAVCollection($aItem) + && \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype'])) { - if (!empty($sKey) && static::hasDAVCollection($aItem) - && \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype'])) - { - $aContactsPaths[$sKey] = isset($aItem['{DAV:}displayname']) ? \trim($aItem['{DAV:}displayname']) : ''; - } + $aContactsPaths[$sPropPath] = isset($aItem['{DAV:}displayname']) ? \trim($aItem['{DAV:}displayname']) : ''; } } } @@ -329,6 +309,9 @@ trait CardDAV return $aContactsPaths; } + /** + * Checks if remote path resourcetype is an addressbook + */ private function checkContactsPath(DAVClient $oClient, string $sPath) : bool { if (!$oClient) @@ -336,16 +319,12 @@ trait CardDAV return false; } - $this->oLogger->Write('PROPFIND '.$sPath, \LOG_INFO, 'DAV'); - $aResponse = null; try { $aResponse = $oClient->propFind($sPath, array( '{DAV:}resourcetype' ), 1); - -// $this->oLogger->WriteDump($aResponse); } catch (\Throwable $oException) { @@ -445,11 +424,13 @@ trait CardDAV $bGood = true; if ('' === $sPath || '/' === $sPath || !$this->checkContactsPath($oClient, $sPath)) { - $sNewPath = ''; - - $aPaths = $this->getContactsPaths($oClient, $sUser, $sPassword, $sProxy); + /** + * Path is not an addressbook, try to find it + */ + $aPaths = $this->getContactsPaths($oClient, $sPath, $sUser, $sPassword, $sProxy); $this->oLogger->WriteDump($aPaths); + $sNewPath = ''; if (\is_array($aPaths)) { if (1 < \count($aPaths)) @@ -493,7 +474,7 @@ trait CardDAV $sPath = $sNewPath; - $bGood = $this->checkContactsPath($oClient, $sPath); + $bGood = $sPath && $this->checkContactsPath($oClient, $sPath); } return $bGood ? $oClient : null; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php index b2b62b25d..c51aa6061 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php @@ -41,7 +41,7 @@ class Client $this->HTTP->setAuth(3, $settings['userName'] ?? '', $settings['password'] ?? ''); $this->HTTP->max_response_kb = 0; $this->HTTP->timeout = 15; // timeout in seconds. -// $this->HTTP->max_redirects = 0; + $this->HTTP->max_redirects = 0; } /** @@ -69,11 +69,12 @@ class Client $url = $this->baseUri . $url; } } + \SnappyMail\Log::debug('DAV', "{$method} {$url}" . ($body ? "\n\t" . \str_replace("\n", "\n\t", $body) : '')); $response = $this->HTTP->doRequest($method, $url, $body, $headers); if (301 == $response->status) { // Like: RewriteRule ^\.well-known/carddav /nextcloud/remote.php/dav [R=301,L] $location = $response->getRedirectLocation(); - \trigger_error("Redirect {$url} to {$location}"); + \SnappyMail\Log::info('DAV', "301 Redirect {$url} to {$location}"); $url = \preg_replace('@^(https?:)?//[^/]+[/$]@', '/', $location); $parts = \parse_url($this->baseUri); $url = $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; @@ -82,6 +83,7 @@ class Client if (300 <= $response->status) { throw new \SnappyMail\HTTP\Exception("{$method} {$url}", $response->status, $response); } + \SnappyMail\Log::debug('DAV', "{$response->status}: {$response->body}"); return $response; } @@ -104,23 +106,20 @@ class Client */ public function propFind(string $url, array $properties, int $depth = 0) : array { - $body = '' . "\n"; - $body.= '' . "\n"; - $body.= ' ' . "\n"; + $body = '' . "\n" . ''; foreach ($properties as $property) { if (!\preg_match('/^{([^}]*)}(.*)$/', $property, $match)) { throw new \InvalidArgumentException('\'' . $property . '\' is not a valid clark-notation formatted string'); } if ('DAV:' === $match[1]) { - $body .= " \n"; + $body .= ""; } else { - $body .= " \n"; + $body .= ""; } } - $body .= ' ' . "\n"; - $body .= ''; + $body .= ''; $response = $this->request('PROPFIND', $url, $body, array( "Depth: {$depth}", @@ -144,7 +143,7 @@ class Client null, LIBXML_NOBLANKS | LIBXML_NOCDATA); if (false === $responseXML) { - throw new \InvalidArgumentException('The passed data is not valid XML'); + throw new \UnexpectedValueException("The passed data is not valid XML\n{$response->body}"); } $ns = \array_search('urn:DAV', $responseXML->getNamespaces(true)) ?: 'd'; @@ -163,13 +162,18 @@ class Client foreach ($propStat->xpath("{$ns}:prop") as $prop) { foreach ($prop->xpath("*") as $element) { $propertyName = self::toClarkNotation($element); + $propList[$propertyName] = []; if ('{DAV:}resourcetype' === $propertyName) { - $propList[$propertyName] = []; foreach ($element->xpath("*") as $resourcetype) { $propList[$propertyName][] = self::toClarkNotation($resourcetype); } } else { - $propList[$propertyName] = (string) $element; + foreach ($element->xpath("*") as $child) { + $propList[$propertyName][self::toClarkNotation($child)] = (string) $child; + } + if (!$propList[$propertyName]) { + $propList[$propertyName] = (string) $element; + } } } } From 38daad6d9bea56f59891b4dcfc8b979f1e07415e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 11:29:00 +0100 Subject: [PATCH 054/114] Disable JSON_PRETTY_PRINT due to issue with \SnappyMail\HTTP\Stream --- snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php index 9aade3928..a736315a5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Utils.php @@ -30,9 +30,11 @@ class Utils public static function jsonEncode($value, int $flags = \JSON_INVALID_UTF8_SUBSTITUTE) : string { try { + /* Issue with \SnappyMail\HTTP\Stream if (Api::Config()->Get('debug', 'enable', false)) { $flags |= \JSON_PRETTY_PRINT; } + */ return \json_encode($value, $flags | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR); } catch (\Throwable $e) { Api::Logger()->WriteException($e, \LOG_ERR, 'JSON'); From 99816083efae961ec72f2d1b95bca0d5401ce2e1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 11:29:46 +0100 Subject: [PATCH 055/114] Add better info to GPG classes --- .../0.0.0/app/libraries/snappymail/pgp/gnupg.php | 2 +- .../v/0.0.0/app/libraries/snappymail/pgp/gpg.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php index a757d186e..947e8f47f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php @@ -10,7 +10,7 @@ class GnuPG { private $homedir, - // Instance of gnupg pecl extension + // Instance of gnupg pecl extension https://www.php.net/gnupg $GnuPG, // Instance of \SnappyMail\PGP\GPG $GPG; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php index 432ec9f84..0173febe7 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gpg.php @@ -188,6 +188,20 @@ class GPG return true; } + /** + * TODO: parse result + * https://github.com/the-djmaze/snappymail/issues/89 + */ + protected function listDecryptKeys(/*string|resource*/ $input, /*string|resource*/ $output = null) + { + $this->setInput($input); + $fclose = $this->setOutput($output); + $_ENV['PINENTRY_USER_DATA'] = ''; + $result = $this->exec(['--list-packets']); + $fclose && \fclose($fclose); + return $output ? true : ($result ? $result['output'] : false); + } + protected function _decrypt(/*string|resource*/ $input, /*string|resource*/ $output = null) { $this->setInput($input); @@ -245,6 +259,7 @@ class GPG */ public function decryptVerify(string $text, string &$plaintext) /*: array|false*/ { + // TODO: https://github.com/the-djmaze/snappymail/issues/89 return false; } @@ -253,6 +268,7 @@ class GPG */ public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/ { + // TODO: https://github.com/the-djmaze/snappymail/issues/89 return false; } From 30577b677afc52ed8b2e9eea39d5f2f0cb4919f8 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 11:30:06 +0100 Subject: [PATCH 056/114] Set $oVCard->VERSION only once --- .../Providers/AddressBook/PdoAddressBook.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index 9a37ee8b2..ae698febb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -531,12 +531,16 @@ class PdoAddressBook $sUid = $sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = ''; $iPrevId = $iId; } - if (!isset($aVCards[$iId])) { - $aVCards[$iId] = new VCard; + if (isset($aVCards[$iId])) { + $oVCard = $aVCards[$iId]; + } else { + $oVCard = new VCard; +// $oVCard = $oVCard->convert(VCard::VCARD40); + $oVCard->VERSION = '4.0'; + $oVCard->PRODID = 'SnappyMail-'.APP_VERSION; + $aVCards[$iId] = $oVCard; } $oVCard = $aVCards[$iId]; - $oVCard->VERSION = '4.0'; - $oVCard->PRODID = 'SnappyMail-'.APP_VERSION; $sPropValue = (string) $aItem['prop_value']; $aTypes = array(); if (!empty($aItem['prop_type_str'])) { From 8cf5bd9c0a44dbf56dfe4c37ebda3682b623acbb Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 11:40:37 +0100 Subject: [PATCH 057/114] Report when user credentials are not set for #653 and #680 --- snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php index c51aa6061..7e19a4510 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/dav/client.php @@ -38,7 +38,11 @@ class Client $this->HTTP = \SnappyMail\HTTP\Request::factory(/*'socket'*/); $this->HTTP->proxy = $settings['proxy'] ?? null; - $this->HTTP->setAuth(3, $settings['userName'] ?? '', $settings['password'] ?? ''); + if (!empty($settings['userName']) && !empty($settings['password'])) { + $this->HTTP->setAuth(3, $settings['userName'], $settings['password']); + } else { + \SnappyMail\Log::warning('DAV', 'No user credentials set'); + } $this->HTTP->max_response_kb = 0; $this->HTTP->timeout = 15; // timeout in seconds. $this->HTTP->max_redirects = 0; From bd287cb749152a26b2760cc5519c5527c550dd13 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 12:12:29 +0100 Subject: [PATCH 058/114] v2.21.3 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 28 +++++++++++++++++++ README.md | 20 ++++++------- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- 8 files changed, 44 insertions(+), 16 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 5ded294e7..9fa455939 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.21.2" +current = "2.21.3" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ad0fa61..acdfe53dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +## 2.21.3 – 2022-11-16 + +### Added +- Click on PGP KEY attachment opens "Import key" dialog + +### Changed +- Increase visible reading area for small screens + [#672](https://github.com/the-djmaze/snappymail/pull/672) +- Improved message spam score detailed view +- Improved DAV connection logging + +### Fixed +- Handling attachments MIME type / content-type +- Message responsive resizing width/height of elements + [#678](https://github.com/the-djmaze/snappymail/pull/678) +- Focus on textarea when creating a new plain text email + [#501](https://github.com/the-djmaze/snappymail/pull/501) +- CardDav remove photos of my contacts when synchronizing + [#679](https://github.com/the-djmaze/snappymail/pull/679) + +### Removed +- \MailSo\Mime\Enumerations\MimeType + +### Nextcloud +- Use fontastic in Nextcloud Files selector dialog +- Firefox < 98 dialogs + [#673](https://github.com/the-djmaze/snappymail/pull/673) + ## 2.21.2 – 2022-11-15 ### Added diff --git a/README.md b/README.md index 89c9940d4..696b181a6 100644 --- a/README.md +++ b/README.md @@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.170.153 | 82.504 | -|app.js |4.207.787 | 406.789 | +|admin.js |2.170.153 | 82.541 | +|app.js |4.207.787 | 406.801 | |boot.js | 868.735 | 1.989 | -|libs.js | 658.812 | 192.965 | +|libs.js | 658.812 | 193.035 | |sieve.js | 0 | 86.121 | |polyfills.js | 334.608 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.240.095 | 770.653 | +|TOTAL |8.240.095 | 770.772 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 256.831 | 40.844 | 73.606 | 13.561 | 60.877 | 12.134 | -|app.min.js | 515.367 | 188.622 |139.456 | 62.735 |110.485 | 53.807 | +|admin.min.js | 256.831 | 40.867 | 73.606 | 13.573 | 60.877 | 12.146 | +|app.min.js | 515.367 | 188.586 |139.456 | 62.800 |110.485 | 53.829 | |boot.min.js | 84.659 | 1.216 | 26.998 | 761 | 23.643 | 614 | -|libs.min.js | 584.772 | 92.042 |180.901 | 34.110 |155.182 | 30.627 | +|libs.min.js | 584.772 | 92.084 |180.901 | 34.119 |155.182 | 30.621 | |sieve.min.js | 0 | 41.926 | 0 | 10.484 | 0 | 9.451 | |polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 | -|TOTAL user |1.217.635 | 281.880 |358.761 | 97.606 |299.485 | 85.048 | -|TOTAL user+sieve |1.217.635 | 323.806 |358.761 |108.090 |299.485 | 94.499 | -|TOTAL admin | 959.099 | 134.102 |292.911 | 48.432 |249.877 | 43.375 | +|TOTAL user |1.217.635 | 281.886 |358.761 | 97.680 |299.485 | 85.064 | +|TOTAL user+sieve |1.217.635 | 323.812 |358.761 |108.164 |299.485 | 94.515 | +|TOTAL admin | 959.099 | 134.167 |292.911 | 48.453 |249.877 | 43.381 | For a user its around 70% smaller and faster than traditional RainLoop. diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 34570f1c6..df4292152 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.21.2. +This app packages SnappyMail 2.21.3. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 4b34c17c4..cf3c28d0d 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.21.2 +VERSION=2.21.3 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index 797556124..2e7f0facb 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.21.2 + 2.21.3 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli Date: Thu, 17 Nov 2022 14:44:34 +0100 Subject: [PATCH 059/114] Bugfix: Contact.display() returns `[object Object]` --- dev/Model/Contact.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js index 9da333509..5b19fdcf2 100644 --- a/dev/Model/Contact.js +++ b/dev/Model/Contact.js @@ -119,7 +119,7 @@ export class ContactModel extends AbstractModel { display: () => { let a = this.fullName(), - b = this.email()?.[0]?.value, + b = this.email()?.[0]?.value(), c = this.nickname(); return a || b || c; } From ecf53b12858dcd3ce4c4a1f50194327a567a912e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 21:04:04 +0100 Subject: [PATCH 060/114] Resolve #361 --- dev/View/User/MailBox/MessageList.js | 33 ++- .../app/libraries/RainLoop/Actions/User.php | 199 ++++++++---------- .../v/0.0.0/app/localization/ar-SA/user.json | 1 + .../v/0.0.0/app/localization/bg-BG/user.json | 1 + .../v/0.0.0/app/localization/cs-CZ/user.json | 1 + .../v/0.0.0/app/localization/da-DK/user.json | 1 + .../v/0.0.0/app/localization/de-DE/user.json | 1 + .../v/0.0.0/app/localization/el-GR/user.json | 1 + .../v/0.0.0/app/localization/en-GB/user.json | 1 + .../v/0.0.0/app/localization/en/user.json | 1 + .../v/0.0.0/app/localization/es-ES/user.json | 1 + .../v/0.0.0/app/localization/et-EE/user.json | 1 + .../v/0.0.0/app/localization/fa-IR/user.json | 1 + .../v/0.0.0/app/localization/fi-FI/user.json | 1 + .../v/0.0.0/app/localization/fr-FR/user.json | 1 + .../v/0.0.0/app/localization/hu-HU/user.json | 1 + .../v/0.0.0/app/localization/id-ID/user.json | 1 + .../v/0.0.0/app/localization/is-IS/user.json | 1 + .../v/0.0.0/app/localization/it-IT/user.json | 1 + .../v/0.0.0/app/localization/ja-JP/user.json | 1 + .../v/0.0.0/app/localization/ko-KR/user.json | 1 + .../v/0.0.0/app/localization/lt-LT/user.json | 1 + .../v/0.0.0/app/localization/lv-LV/user.json | 1 + .../v/0.0.0/app/localization/nb-NO/user.json | 1 + .../v/0.0.0/app/localization/nl-NL/user.json | 1 + .../v/0.0.0/app/localization/pl-PL/user.json | 1 + .../v/0.0.0/app/localization/pt-BR/user.json | 1 + .../v/0.0.0/app/localization/pt-PT/user.json | 1 + .../v/0.0.0/app/localization/pt/user.json | 1 + .../v/0.0.0/app/localization/ro-RO/user.json | 1 + .../v/0.0.0/app/localization/ru-RU/user.json | 1 + .../v/0.0.0/app/localization/sk-SK/user.json | 1 + .../v/0.0.0/app/localization/sl-SI/user.json | 1 + .../v/0.0.0/app/localization/sv-SE/user.json | 1 + .../v/0.0.0/app/localization/tr-TR/user.json | 1 + .../v/0.0.0/app/localization/uk-UA/user.json | 1 + .../v/0.0.0/app/localization/vi-VN/user.json | 1 + .../v/0.0.0/app/localization/zh-CN/user.json | 1 + .../v/0.0.0/app/localization/zh-TW/user.json | 1 + .../templates/Views/User/MailMessageList.html | 3 + 40 files changed, 161 insertions(+), 111 deletions(-) diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index c8009c018..5de188bda 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -11,11 +11,11 @@ import { leftPanelDisabled, moveAction, addShortcut, registerShortcut, formFieldFocused } from 'Common/Globals'; -import { computedPaginatorHelper, showMessageComposer, populateMessageBody } from 'Common/UtilsUser'; +import { computedPaginatorHelper, showMessageComposer, populateMessageBody, download } from 'Common/UtilsUser'; import { FileInfo } from 'Common/File'; import { isFullscreen, toggleFullscreen } from 'Common/Fullscreen'; -import { mailBox } from 'Common/Links'; +import { mailBox, attachmentDownload } from 'Common/Links'; import { Selector } from 'Common/Selector'; import { i18n } from 'Common/Translator'; @@ -258,6 +258,7 @@ export class MailMessageList extends AbstractViewRight { ).throttle(50)); decorateKoCommands(this, { + downloadCommand: canBeMovedHelper, forwardCommand: canBeMovedHelper, deleteWithoutMoveCommand: canBeMovedHelper, deleteCommand: canBeMovedHelper, @@ -290,6 +291,34 @@ export class MailMessageList extends AbstractViewRight { ]); } + downloadCommand() { + let hashes = []; + MessagelistUserStore.forEach(message => { + if (message.checked()) { + message.attachments.forEach(attachment => { + if (!attachment.isLinked() && attachment.download) { + hashes.push(attachment.download); + } + }); + } + }); + if (hashes.length) { + Remote.post('AttachmentsActions', null, { + Do: 'Zip', + Hashes: hashes + }) + .then(result => { + let hash = result?.Result?.FileHash; + if (hash) { + download(attachmentDownload(hash), hash+'.zip'); + } else { + alert('Download failed'); + } + }) + .catch(() => alert('Download failed')); + } + } + deleteWithoutMoveCommand() { SettingsCapa('DangerousActions') && moveMessagesToFolderType(FolderType.Trash, true); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php index 9f0e0b574..39d1234f0 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -73,142 +73,123 @@ trait User */ public function DoAttachmentsActions() : array { - if (!$this->GetCapa(Capa::ATTACHMENTS_ACTIONS)) - { + $sAction = $this->GetActionParam('Do', ''); + $aHashes = $this->GetActionParam('Hashes', null); + $oFilesProvider = $this->FilesProvider(); + if (empty($sAction) || !$this->GetCapa(Capa::ATTACHMENTS_ACTIONS) || !$oFilesProvider || !$oFilesProvider->IsActive()) { return $this->FalseResponse(__FUNCTION__); } $oAccount = $this->initMailClientConnection(); - $sAction = $this->GetActionParam('Do', ''); - $aHashes = $this->GetActionParam('Hashes', null); - - $mResult = false; $bError = false; - $aData = false; + $aData = []; + $mUIDs = []; - if (\is_array($aHashes) && \count($aHashes)) - { - $aData = array(); - foreach ($aHashes as $sZipHash) - { + if (\is_array($aHashes) && \count($aHashes)) { + foreach ($aHashes as $sZipHash) { $aResult = $this->getMimeFileByHash($oAccount, $sZipHash); - if (!empty($aResult['FileHash'])) - { - $aData[] = $aResult; - } - else - { + if (empty($aResult['FileHash'])) { $bError = true; break; } + $aData[] = $aResult; + $mUIDs[$aResult['Uid']] = $aResult['Uid']; } } + $mUIDs = 1 < \count($mUIDs); - $oFilesProvider = $this->FilesProvider(); - if (!empty($sAction) && !$bError && \is_array($aData) && \count($aData) && - $oFilesProvider && $oFilesProvider->IsActive()) + if ($bError || !\count($aData)) { + return $this->FalseResponse(__FUNCTION__); + } + + $mResult = false; + switch (\strtolower($sAction)) { - $bError = false; - switch (\strtolower($sAction)) - { - case 'zip': + case 'zip': - $sZipHash = \MailSo\Base\Utils::Sha1Rand(); - $sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash); - - if (!empty($sZipFileName)) { - if (\class_exists('ZipArchive')) { - $oZip = new \ZipArchive(); - $oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE); - $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); - foreach ($aData as $aItem) { - $sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'); - $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); - if (!empty($sFileHash)) { - $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); - if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { - $bError = true; - } - } - } - - if (!$bError) { - $bError = !$oZip->close(); - } else { - $oZip->close(); - } -/* - } else { - @\unlink($sZipFileName); - $oZip = new \SnappyMail\Stream\ZIP($sZipFileName); -// $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); - foreach ($aData as $aItem) { - $sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'); - $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); - if (!empty($sFileHash)) { - $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); - if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { - $bError = true; - } - } - } - $oZip->close(); -*/ - } else { - @\unlink($sZipFileName); - $oZip = new \PharData($sZipFileName . '.zip', 0, null, \Phar::ZIP); - $oZip->compressFiles(\Phar::GZ); - foreach ($aData as $aItem) { - $sFileName = (isset($aItem['FileName']) ? (string) $aItem['FileName'] : 'file.dat'); - $sFileHash = (isset($aItem['FileHash']) ? (string) $aItem['FileHash'] : ''); - if ($sFileHash) { - $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); - $oZip->addFile($sFullFileNameHash, $sFileName); - } - } - $oZip->compressFiles(\Phar::GZ); - unset($oZip); - \rename($sZipFileName . '.zip', $sZipFileName); - } + $sZipHash = \MailSo\Base\Utils::Sha1Rand(); + $sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash); + if (!empty($sZipFileName)) { + if (\class_exists('ZipArchive')) { + $oZip = new \ZipArchive(); + $oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE); + $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); foreach ($aData as $aItem) { - $sFileHash = (isset($aItem['FileHash']) ? (string) $aItem['FileHash'] : ''); - if ($sFileHash) { - $oFilesProvider->Clear($oAccount, $sFileHash); + $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $aItem['FileHash']); + $sFileName = ($mUIDs ? "{$aItem['Uid']}/" : '') . ($aItem['FileName'] ?: 'file.dat'); + if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { + $bError = true; } } - if (!$bError) { - $mResult = array( - 'FileHash' => Utils::EncodeKeyValuesQ(array( - 'Account' => $oAccount ? $oAccount->Hash() : '', - 'FileName' => 'attachments.zip', - 'MimeType' => 'application/zip', - 'FileHash' => $sZipHash - )) + if ($bError) { + $oZip->close(); + } else { + $bError = !$oZip->close(); + } +/* + } else { + @\unlink($sZipFileName); + $oZip = new \SnappyMail\Stream\ZIP($sZipFileName); +// $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); + foreach ($aData as $aItem) { + $sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'); + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + if (!empty($sFileHash)) { + $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); + if (!$oZip->addFile($sFullFileNameHash, $sFileName)) { + $bError = true; + } + } + } + $oZip->close(); +*/ + } else { + @\unlink($sZipFileName); + $oZip = new \PharData($sZipFileName . '.zip', 0, null, \Phar::ZIP); + $oZip->compressFiles(\Phar::GZ); + foreach ($aData as $aItem) { + $oZip->addFile( + $oFilesProvider->GetFileName($oAccount, $aItem['FileHash']), + ($mUIDs ? "{$aItem['Uid']}/" : '') . ($aItem['FileName'] ?: 'file.dat') ); } + $oZip->compressFiles(\Phar::GZ); + unset($oZip); + \rename($sZipFileName . '.zip', $sZipFileName); } - break; - default: - $data = new \SnappyMail\AttachmentsAction; - $data->action = $sAction; - $data->items = $aData; - $data->filesProvider = $oFilesProvider; - $data->account = $oAccount; - $this->Plugins()->RunHook('json.attachments', array($data)); - $mResult = $data->result; - break; - } - } - else - { - $bError = true; + foreach ($aData as $aItem) { + $oFilesProvider->Clear($oAccount, $aItem['FileHash']); + } + + if (!$bError) { + $mResult = array( + 'FileHash' => Utils::EncodeKeyValuesQ(array( + 'Account' => $oAccount ? $oAccount->Hash() : '', + 'FileName' => 'attachments.zip', + 'MimeType' => 'application/zip', + 'FileHash' => $sZipHash + )) + ); + } + } + break; + + default: + $data = new \SnappyMail\AttachmentsAction; + $data->action = $sAction; + $data->items = $aData; + $data->filesProvider = $oFilesProvider; + $data->account = $oAccount; + $this->Plugins()->RunHook('json.attachments', array($data)); + $mResult = $data->result; + break; } - $this->requestSleep(); +// $this->requestSleep(); return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult); } diff --git a/snappymail/v/0.0.0/app/localization/ar-SA/user.json b/snappymail/v/0.0.0/app/localization/ar-SA/user.json index ca725bdd8..c475bfff2 100644 --- a/snappymail/v/0.0.0/app/localization/ar-SA/user.json +++ b/snappymail/v/0.0.0/app/localization/ar-SA/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "مقروء", "MENU_SELECT_FLAGGED": "محدد كـ مهم", "MENU_SELECT_UNFLAGGED": "إزالة الأهمية", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "إفراغ القائمة", "EMPTY_SEARCH_LIST": "ليست هناك أية رسائل تطابق بحثك.", "SEARCH_RESULT_FOR": "نتيجة البحث لـ \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/bg-BG/user.json b/snappymail/v/0.0.0/app/localization/bg-BG/user.json index 0387c2e04..e4fde44ee 100644 --- a/snappymail/v/0.0.0/app/localization/bg-BG/user.json +++ b/snappymail/v/0.0.0/app/localization/bg-BG/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Прочетени", "MENU_SELECT_FLAGGED": "Маркирани с флаг", "MENU_SELECT_UNFLAGGED": "С премахнат флаг", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Празен списък.", "EMPTY_SEARCH_LIST": "Няма съобщения, които да отговарят на търсенето.", "SEARCH_RESULT_FOR": "Резултати от търсенето за \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json index 710bf9b83..4dbc983c6 100644 --- a/snappymail/v/0.0.0/app/localization/cs-CZ/user.json +++ b/snappymail/v/0.0.0/app/localization/cs-CZ/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Přečtené", "MENU_SELECT_FLAGGED": "Označené", "MENU_SELECT_UNFLAGGED": "Neoznačené", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Seznam je prázdný", "EMPTY_SEARCH_LIST": "Vašemu vyhledávání neodpovídají žádné zprávy.", "SEARCH_RESULT_FOR": "Výsledky vyhledávání pro \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/da-DK/user.json b/snappymail/v/0.0.0/app/localization/da-DK/user.json index e38f7e96d..4188896ba 100644 --- a/snappymail/v/0.0.0/app/localization/da-DK/user.json +++ b/snappymail/v/0.0.0/app/localization/da-DK/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Læst", "MENU_SELECT_FLAGGED": "Markeret med flag", "MENU_SELECT_UNFLAGGED": "Ikke markeret med flag", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Ingen meddelelser.", "EMPTY_SEARCH_LIST": "Ingen meddelelser passede på søgningen.", "SEARCH_RESULT_FOR": "Resultat for søgningen på \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/de-DE/user.json b/snappymail/v/0.0.0/app/localization/de-DE/user.json index d137ff824..0d0df500f 100644 --- a/snappymail/v/0.0.0/app/localization/de-DE/user.json +++ b/snappymail/v/0.0.0/app/localization/de-DE/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Gelesen", "MENU_SELECT_FLAGGED": "Markiert", "MENU_SELECT_UNFLAGGED": "Nicht markiert", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Liste ist leer.", "EMPTY_SEARCH_LIST": "Keine Treffer für Ihre Suche.", "SEARCH_RESULT_FOR": "Suchergebnisse für \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/el-GR/user.json b/snappymail/v/0.0.0/app/localization/el-GR/user.json index b078346fc..f13a8fef9 100644 --- a/snappymail/v/0.0.0/app/localization/el-GR/user.json +++ b/snappymail/v/0.0.0/app/localization/el-GR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Διαβασμένο", "MENU_SELECT_FLAGGED": "Σημαδεμένο", "MENU_SELECT_UNFLAGGED": "Μη σημαδεμένο", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Άδεια λίστα.", "EMPTY_SEARCH_LIST": "Δεν υπάρχουν μηνύματα που να ταιριάζουν στην αναζήτηση.", "SEARCH_RESULT_FOR": "Αποτελέσματα αναζήτησης για \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/en-GB/user.json b/snappymail/v/0.0.0/app/localization/en-GB/user.json index a4f02da73..9a350cfdc 100644 --- a/snappymail/v/0.0.0/app/localization/en-GB/user.json +++ b/snappymail/v/0.0.0/app/localization/en-GB/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Read", "MENU_SELECT_FLAGGED": "Flagged", "MENU_SELECT_UNFLAGGED": "Unflagged", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Empty list.", "EMPTY_SEARCH_LIST": "No messages matched your search.", "SEARCH_RESULT_FOR": "Search results for \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/en/user.json b/snappymail/v/0.0.0/app/localization/en/user.json index 0b91cef9d..97a8dc596 100644 --- a/snappymail/v/0.0.0/app/localization/en/user.json +++ b/snappymail/v/0.0.0/app/localization/en/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Read", "MENU_SELECT_FLAGGED": "Flagged", "MENU_SELECT_UNFLAGGED": "Unflagged", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Empty list.", "EMPTY_SEARCH_LIST": "No messages matched your search.", "SEARCH_RESULT_FOR": "Search results for \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/es-ES/user.json b/snappymail/v/0.0.0/app/localization/es-ES/user.json index 0b158dab2..03eab2015 100644 --- a/snappymail/v/0.0.0/app/localization/es-ES/user.json +++ b/snappymail/v/0.0.0/app/localization/es-ES/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Leído", "MENU_SELECT_FLAGGED": "Destacado", "MENU_SELECT_UNFLAGGED": "No Destacado", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "No hay mensajes.", "EMPTY_SEARCH_LIST": "La búsqueda no ha producido resultados.", "SEARCH_RESULT_FOR": "Resultados de búsqueda para \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/et-EE/user.json b/snappymail/v/0.0.0/app/localization/et-EE/user.json index 9fd65a483..544fdd3b1 100644 --- a/snappymail/v/0.0.0/app/localization/et-EE/user.json +++ b/snappymail/v/0.0.0/app/localization/et-EE/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Loetud", "MENU_SELECT_FLAGGED": "Lipuga märgitud", "MENU_SELECT_UNFLAGGED": "Lipuga märkimata", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tühi nimekiri", "EMPTY_SEARCH_LIST": "Otsingule ei vastanud ühtegi kirja", "SEARCH_RESULT_FOR": "Vasted otsingule \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/fa-IR/user.json b/snappymail/v/0.0.0/app/localization/fa-IR/user.json index 99c6e5bbb..e4f8f09c3 100644 --- a/snappymail/v/0.0.0/app/localization/fa-IR/user.json +++ b/snappymail/v/0.0.0/app/localization/fa-IR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "خوانده", "MENU_SELECT_FLAGGED": "پرچم‌دار کردن", "MENU_SELECT_UNFLAGGED": "پرچم‌دار نشده", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "فهرست خالی", "EMPTY_SEARCH_LIST": "پیامی مطابق جستجوی شما پیدا نشد", "SEARCH_RESULT_FOR": "نتایج جستجو مطابق \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/fi-FI/user.json b/snappymail/v/0.0.0/app/localization/fi-FI/user.json index 036ad01ea..92150c335 100644 --- a/snappymail/v/0.0.0/app/localization/fi-FI/user.json +++ b/snappymail/v/0.0.0/app/localization/fi-FI/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Luetut", "MENU_SELECT_FLAGGED": "Merkityt", "MENU_SELECT_UNFLAGGED": "Merkkaamattomat", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tyhjä lista", "EMPTY_SEARCH_LIST": "Yhtään viestiä ei löytynyt ehdoillasi.", "SEARCH_RESULT_FOR": "Hakutulokset haulle \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/fr-FR/user.json b/snappymail/v/0.0.0/app/localization/fr-FR/user.json index f11f3f06e..df31762ba 100644 --- a/snappymail/v/0.0.0/app/localization/fr-FR/user.json +++ b/snappymail/v/0.0.0/app/localization/fr-FR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lu", "MENU_SELECT_FLAGGED": "Signalés", "MENU_SELECT_UNFLAGGED": "Non signalés", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Liste vide.", "EMPTY_SEARCH_LIST": "Aucun message ne correspond à votre recherche.", "SEARCH_RESULT_FOR": "Résultats de recherche pour \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/hu-HU/user.json b/snappymail/v/0.0.0/app/localization/hu-HU/user.json index 1704fa9f9..c1d4085e3 100644 --- a/snappymail/v/0.0.0/app/localization/hu-HU/user.json +++ b/snappymail/v/0.0.0/app/localization/hu-HU/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Olvasott", "MENU_SELECT_FLAGGED": "Megjelölt", "MENU_SELECT_UNFLAGGED": "Jelöletlen", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Lista üres.", "EMPTY_SEARCH_LIST": "Nincs a keresésnek megfelelő levél.", "SEARCH_RESULT_FOR": "Keresési eredmények ehhez: \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/id-ID/user.json b/snappymail/v/0.0.0/app/localization/id-ID/user.json index 2645efdc9..27751d2a6 100644 --- a/snappymail/v/0.0.0/app/localization/id-ID/user.json +++ b/snappymail/v/0.0.0/app/localization/id-ID/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Dibaca", "MENU_SELECT_FLAGGED": "Ditandai", "MENU_SELECT_UNFLAGGED": "Jangan tandai", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Daftar kosong", "EMPTY_SEARCH_LIST": "Tidak ada pesan yang sesuai dengan pencarian", "SEARCH_RESULT_FOR": "Hasil pencarian untuk \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/is-IS/user.json b/snappymail/v/0.0.0/app/localization/is-IS/user.json index c01abbe86..89d3f163a 100644 --- a/snappymail/v/0.0.0/app/localization/is-IS/user.json +++ b/snappymail/v/0.0.0/app/localization/is-IS/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lesið", "MENU_SELECT_FLAGGED": "Flaggað", "MENU_SELECT_UNFLAGGED": "Afflaggað", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tómur listi.", "EMPTY_SEARCH_LIST": "Engin skilaboð stemmdu við leitina þína.", "SEARCH_RESULT_FOR": "Leitarniðurstöður fyrir \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/it-IT/user.json b/snappymail/v/0.0.0/app/localization/it-IT/user.json index 7ad06fe51..0130c254f 100644 --- a/snappymail/v/0.0.0/app/localization/it-IT/user.json +++ b/snappymail/v/0.0.0/app/localization/it-IT/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Letti", "MENU_SELECT_FLAGGED": "Preferiti", "MENU_SELECT_UNFLAGGED": "Non preferiti", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Lista vuota", "EMPTY_SEARCH_LIST": "Nessun messaggio corrisponde alla tua ricerca", "SEARCH_RESULT_FOR": "Risultati di ricerca per \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/ja-JP/user.json b/snappymail/v/0.0.0/app/localization/ja-JP/user.json index b32e8ed5b..d5d161c88 100644 --- a/snappymail/v/0.0.0/app/localization/ja-JP/user.json +++ b/snappymail/v/0.0.0/app/localization/ja-JP/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "既読", "MENU_SELECT_FLAGGED": "フラグ付き", "MENU_SELECT_UNFLAGGED": "フラグなし", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "メッセージはありません", "EMPTY_SEARCH_LIST": "検索条件に一致するメールは見つかりませんでした。高度な検索を試してみてください。", "SEARCH_RESULT_FOR": "\"%SEARCH%\"の検索結果", diff --git a/snappymail/v/0.0.0/app/localization/ko-KR/user.json b/snappymail/v/0.0.0/app/localization/ko-KR/user.json index 7451fc0d0..39f8d2e61 100644 --- a/snappymail/v/0.0.0/app/localization/ko-KR/user.json +++ b/snappymail/v/0.0.0/app/localization/ko-KR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "읽음", "MENU_SELECT_FLAGGED": "중요", "MENU_SELECT_UNFLAGGED": "일반", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "목록이 비어있습니다.", "EMPTY_SEARCH_LIST": "검색 조건에 맞는 결과가 없습니다..", "SEARCH_RESULT_FOR": "\"%SEARCH%\"에 대한 검색 결과", diff --git a/snappymail/v/0.0.0/app/localization/lt-LT/user.json b/snappymail/v/0.0.0/app/localization/lt-LT/user.json index 84f5e84cb..ce340eb26 100644 --- a/snappymail/v/0.0.0/app/localization/lt-LT/user.json +++ b/snappymail/v/0.0.0/app/localization/lt-LT/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Skaityta", "MENU_SELECT_FLAGGED": "Pažymėta", "MENU_SELECT_UNFLAGGED": "Nepažymėta", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tuščias sąrašas.", "EMPTY_SEARCH_LIST": "Nėra pranešimų, atitinkančių jūsų paiešką.", "SEARCH_RESULT_FOR": "Ieškoti \"%SEARCH%\" rezultatuose", diff --git a/snappymail/v/0.0.0/app/localization/lv-LV/user.json b/snappymail/v/0.0.0/app/localization/lv-LV/user.json index 21d45ba96..6a8d5d474 100644 --- a/snappymail/v/0.0.0/app/localization/lv-LV/user.json +++ b/snappymail/v/0.0.0/app/localization/lv-LV/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Izlasīts", "MENU_SELECT_FLAGGED": "Atzīmētie", "MENU_SELECT_UNFLAGGED": "Noņemtās atzīmes", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tukšs saraksts.", "EMPTY_SEARCH_LIST": "Neviens ziņojums neatbilda jūsu meklētajiem kritērijiem.", "SEARCH_RESULT_FOR": "Meklēšanas rezultāti: \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/nb-NO/user.json b/snappymail/v/0.0.0/app/localization/nb-NO/user.json index 029943f2f..f1302a641 100644 --- a/snappymail/v/0.0.0/app/localization/nb-NO/user.json +++ b/snappymail/v/0.0.0/app/localization/nb-NO/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lest", "MENU_SELECT_FLAGGED": "Stjernemerket", "MENU_SELECT_UNFLAGGED": "Ikke stjernemerket", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tom liste", "EMPTY_SEARCH_LIST": "Søket ga ingen treff.", "SEARCH_RESULT_FOR": "Søketreff for «%SEARCH%»", diff --git a/snappymail/v/0.0.0/app/localization/nl-NL/user.json b/snappymail/v/0.0.0/app/localization/nl-NL/user.json index f460e51c3..43f7c32e0 100644 --- a/snappymail/v/0.0.0/app/localization/nl-NL/user.json +++ b/snappymail/v/0.0.0/app/localization/nl-NL/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Gelezen", "MENU_SELECT_FLAGGED": "Met Vlag", "MENU_SELECT_UNFLAGGED": "Zonder Vlag", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Geen berichten", "EMPTY_SEARCH_LIST": "Geen items gevonden", "SEARCH_RESULT_FOR": "Zoekresultaten voor \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/pl-PL/user.json b/snappymail/v/0.0.0/app/localization/pl-PL/user.json index 5bfc17f1f..e34938f38 100644 --- a/snappymail/v/0.0.0/app/localization/pl-PL/user.json +++ b/snappymail/v/0.0.0/app/localization/pl-PL/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Przeczytane", "MENU_SELECT_FLAGGED": "Oznaczone jako ważne", "MENU_SELECT_UNFLAGGED": "Oznaczone jako nieważne", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Brak wiadomości.", "EMPTY_SEARCH_LIST": "Brak wiadomości spełniających kryteria wyszukiwania.", "SEARCH_RESULT_FOR": "Wyniki wyszukiwania dla \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/pt-BR/user.json b/snappymail/v/0.0.0/app/localization/pt-BR/user.json index ba185bbe8..d1fc7823d 100644 --- a/snappymail/v/0.0.0/app/localization/pt-BR/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-BR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lidas", "MENU_SELECT_FLAGGED": "Sinalizadas", "MENU_SELECT_UNFLAGGED": "Não Sinalizadas", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Lista Vazia.", "EMPTY_SEARCH_LIST": "Nenhuma mensagem corresponde a sua pesquisa.", "SEARCH_RESULT_FOR": "Resultados de pesquisa para \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/pt-PT/user.json b/snappymail/v/0.0.0/app/localization/pt-PT/user.json index 345886cb4..73c6c95b9 100644 --- a/snappymail/v/0.0.0/app/localization/pt-PT/user.json +++ b/snappymail/v/0.0.0/app/localization/pt-PT/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lida", "MENU_SELECT_FLAGGED": "Sinalizada", "MENU_SELECT_UNFLAGGED": "Não sinalizada", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Lista vazia.", "EMPTY_SEARCH_LIST": "Nenhuma mensagem corresponde à sua pesquisa.", "SEARCH_RESULT_FOR": "Resultados de pesquisa para \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/pt/user.json b/snappymail/v/0.0.0/app/localization/pt/user.json index 345886cb4..73c6c95b9 100644 --- a/snappymail/v/0.0.0/app/localization/pt/user.json +++ b/snappymail/v/0.0.0/app/localization/pt/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Lida", "MENU_SELECT_FLAGGED": "Sinalizada", "MENU_SELECT_UNFLAGGED": "Não sinalizada", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Lista vazia.", "EMPTY_SEARCH_LIST": "Nenhuma mensagem corresponde à sua pesquisa.", "SEARCH_RESULT_FOR": "Resultados de pesquisa para \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/ro-RO/user.json b/snappymail/v/0.0.0/app/localization/ro-RO/user.json index e0ccad867..ec18726bc 100644 --- a/snappymail/v/0.0.0/app/localization/ro-RO/user.json +++ b/snappymail/v/0.0.0/app/localization/ro-RO/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Citit", "MENU_SELECT_FLAGGED": "Important", "MENU_SELECT_UNFLAGGED": "Neimportant", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Nimic aici, deocamdată", "EMPTY_SEARCH_LIST": "Nu am găsit nimic.", "SEARCH_RESULT_FOR": "Rezultat căutare \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/ru-RU/user.json b/snappymail/v/0.0.0/app/localization/ru-RU/user.json index bd0f2cdf1..9522c2674 100644 --- a/snappymail/v/0.0.0/app/localization/ru-RU/user.json +++ b/snappymail/v/0.0.0/app/localization/ru-RU/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Прочитанные", "MENU_SELECT_FLAGGED": "Помеченные", "MENU_SELECT_UNFLAGGED": "Без пометок", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "В папке нет писем.", "EMPTY_SEARCH_LIST": "Письма не найдены.", "SEARCH_RESULT_FOR": "Результат поиска \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/sk-SK/user.json b/snappymail/v/0.0.0/app/localization/sk-SK/user.json index 95486aa95..178fa7c1a 100644 --- a/snappymail/v/0.0.0/app/localization/sk-SK/user.json +++ b/snappymail/v/0.0.0/app/localization/sk-SK/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Prečítané", "MENU_SELECT_FLAGGED": "Označené", "MENU_SELECT_UNFLAGGED": "Neoznačené", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Zoznam je prázdny.", "EMPTY_SEARCH_LIST": "Vášmu vyhľadávaniu nezodpovedajú žiadne správy.", "SEARCH_RESULT_FOR": "Výsledky vyhľadávania pre \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/sl-SI/user.json b/snappymail/v/0.0.0/app/localization/sl-SI/user.json index 80b315dcb..7c8746ca7 100644 --- a/snappymail/v/0.0.0/app/localization/sl-SI/user.json +++ b/snappymail/v/0.0.0/app/localization/sl-SI/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Prebrana", "MENU_SELECT_FLAGGED": "Z zvezdico", "MENU_SELECT_UNFLAGGED": "Brez zvezdice", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Prazen seznam.", "EMPTY_SEARCH_LIST": "Brez ujemanj.", "SEARCH_RESULT_FOR": "Ujemanja za \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/sv-SE/user.json b/snappymail/v/0.0.0/app/localization/sv-SE/user.json index e0ecc7729..6fb1a1717 100644 --- a/snappymail/v/0.0.0/app/localization/sv-SE/user.json +++ b/snappymail/v/0.0.0/app/localization/sv-SE/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Läst", "MENU_SELECT_FLAGGED": "Flaggad", "MENU_SELECT_UNFLAGGED": "Avflaggad", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Tom lista", "EMPTY_SEARCH_LIST": "Inga meddelanden matchar din sökning.", "SEARCH_RESULT_FOR": "Sök resultat för \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/tr-TR/user.json b/snappymail/v/0.0.0/app/localization/tr-TR/user.json index c53a5a038..e67350a55 100644 --- a/snappymail/v/0.0.0/app/localization/tr-TR/user.json +++ b/snappymail/v/0.0.0/app/localization/tr-TR/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Oku", "MENU_SELECT_FLAGGED": "İşaretlenmiş", "MENU_SELECT_UNFLAGGED": "İşaretlenmemiş", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Herhangi Bir Sonuç Bulunamadı.", "EMPTY_SEARCH_LIST": "Arama sonuçlarına uyan herhangi bir mesaj bulunamadı.", "SEARCH_RESULT_FOR": "\"%SEARCH%\" için arama sonuçları", diff --git a/snappymail/v/0.0.0/app/localization/uk-UA/user.json b/snappymail/v/0.0.0/app/localization/uk-UA/user.json index 4a3616a98..18a1db642 100644 --- a/snappymail/v/0.0.0/app/localization/uk-UA/user.json +++ b/snappymail/v/0.0.0/app/localization/uk-UA/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Прочитані", "MENU_SELECT_FLAGGED": "Відмічені", "MENU_SELECT_UNFLAGGED": "Без поміток", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "В папці немає листів.", "EMPTY_SEARCH_LIST": "Листи не знайдено.", "SEARCH_RESULT_FOR": "Результат пошуку \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/vi-VN/user.json b/snappymail/v/0.0.0/app/localization/vi-VN/user.json index 025d88a90..a48a9666d 100644 --- a/snappymail/v/0.0.0/app/localization/vi-VN/user.json +++ b/snappymail/v/0.0.0/app/localization/vi-VN/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "Đã đọc", "MENU_SELECT_FLAGGED": "Được đánh dấu sao", "MENU_SELECT_UNFLAGGED": "Không được đánh dấu sao", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "Danh sách trống.", "EMPTY_SEARCH_LIST": "Không tìm thấy thư trùng khớp.", "SEARCH_RESULT_FOR": "Kết quả tìm kiếm cho \"%SEARCH%\"", diff --git a/snappymail/v/0.0.0/app/localization/zh-CN/user.json b/snappymail/v/0.0.0/app/localization/zh-CN/user.json index bd51798dc..5750a297f 100644 --- a/snappymail/v/0.0.0/app/localization/zh-CN/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-CN/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "已读", "MENU_SELECT_FLAGGED": "已加星标", "MENU_SELECT_UNFLAGGED": "未加星标", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "空列表。", "EMPTY_SEARCH_LIST": "未找到您搜索的消息。", "SEARCH_RESULT_FOR": "搜索 \"%SEARCH%\" 的结果", diff --git a/snappymail/v/0.0.0/app/localization/zh-TW/user.json b/snappymail/v/0.0.0/app/localization/zh-TW/user.json index 2f167ae31..cbd23c4b4 100644 --- a/snappymail/v/0.0.0/app/localization/zh-TW/user.json +++ b/snappymail/v/0.0.0/app/localization/zh-TW/user.json @@ -98,6 +98,7 @@ "MENU_SELECT_SEEN": "已讀", "MENU_SELECT_FLAGGED": "已加星標", "MENU_SELECT_UNFLAGGED": "未加星標", + "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", "EMPTY_LIST": "空列表。", "EMPTY_SEARCH_LIST": "未找到您想要的。", "SEARCH_RESULT_FOR": "搜索 \"%SEARCH%\" 的結果", diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index f751a7f03..93629708f 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -43,6 +43,9 @@ +
  • + +
  • From 7da3ba8c614df49402b6bd5b650f4c18bf4d8831 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Nov 2022 21:40:35 +0100 Subject: [PATCH 061/114] Cleanup and rearrange some fontastic glyphs --- dev/View/User/MailBox/MessageView.js | 4 ++-- .../templates/Views/User/MailMessageList.html | 2 +- .../templates/Views/User/MailMessageView.html | 5 +---- .../templates/Views/User/SystemDropDown.html | 5 +---- vendors/fontastic/fonts/snappymail.woff | Bin 10088 -> 10024 bytes vendors/fontastic/fonts/snappymail.woff2 | Bin 8036 -> 7968 bytes vendors/fontastic/styles.css | 17 ++++------------- 7 files changed, 9 insertions(+), 24 deletions(-) diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 0c7e90cf7..aca6556e9 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -162,9 +162,9 @@ export class MailMessageView extends AbstractViewRight { case 'none': return ''; case 'pass': - return 'icon-ok iconcolor-green'; + return 'icon-ok iconcolor-green'; // ✔️ default: - return 'icon-warning-alt iconcolor-red'; + return 'icon-cross iconcolor-red'; // ✖ ❌ } }, diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html index 93629708f..92091d5e8 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailMessageList.html @@ -142,7 +142,7 @@
    -
     
    +
     
    -
    diff --git a/snappymail/v/0.0.0/app/templates/Views/User/Login.html b/snappymail/v/0.0.0/app/templates/Views/User/Login.html index d662ee127..051d9bb6c 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/Login.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/Login.html @@ -38,7 +38,7 @@
    -
    From d468bac5515a6985071348429ab97b0e1b1478f4 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 21 Nov 2022 13:20:59 +0100 Subject: [PATCH 071/114] Log current shortcuts scope for #690 --- dev/shortcuts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/shortcuts.js b/dev/shortcuts.js index 0ff31b78d..d993b7300 100644 --- a/dev/shortcuts.js +++ b/dev/shortcuts.js @@ -56,6 +56,7 @@ const setScope: value => { _scope = value || 'all'; scope = _scopes[_scope] || {}; + console.log('Shortcuts scope set to: ' + _scope); }, getScope: () => _scope, getMetaKey: () => 'meta' === meta ? '⌘' : 'Ctrl' From 08baff9e64654a737aa8953b0d491025411c1371 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 21 Nov 2022 17:18:11 +0100 Subject: [PATCH 072/114] Added domain matcher test for #689 --- dev/Settings/Admin/Domains.js | 18 ++++++++++- .../libraries/RainLoop/Actions/UserAuth.php | 32 +++++++++++-------- .../app/libraries/RainLoop/ActionsAdmin.php | 17 ++++++++++ .../Views/Admin/AdminSettingsDomains.html | 4 +++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/dev/Settings/Admin/Domains.js b/dev/Settings/Admin/Domains.js index e248d7063..cbaa0888a 100644 --- a/dev/Settings/Admin/Domains.js +++ b/dev/Settings/Admin/Domains.js @@ -11,10 +11,26 @@ import { DomainAliasPopupView } from 'View/Popup/DomainAlias'; export class AdminSettingsDomains /*extends AbstractViewSettings*/ { constructor() { this.domains = DomainAdminStore; - + this.username = ko.observable(''); this.domainForDeletion = ko.observable(null).askDeleteHelper(); } + testUsername() { + // TODO: find domain matching username + Remote.request('AdminDomainMatch', + (iError, oData) => { + if (oData?.Result?.domain) { + alert('Matched domain: ' + oData.Result.domain.name); + } else { + alert('No domain match'); + } + }, + { + username: this.username() + } + ); + } + createDomain() { showScreenPopup(DomainPopupView); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php index 5d93c2117..ebb1ffe65 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php @@ -21,10 +21,8 @@ trait UserAuth /** * @throws \RainLoop\Exceptions\ClientException */ - public function LoginProcess(string &$sEmail, string &$sPassword, bool $bSignMe = false, bool $bMainAccount = true): Account + protected function resolveLoginCredentials(string &$sEmail, string &$sPassword, string &$sLogin): void { - $sInputEmail = $sEmail; - $this->Plugins()->RunHook('login.credentials.step-1', array(&$sEmail)); $sEmail = \MailSo\Base\Utils::Trim($sEmail); @@ -101,22 +99,30 @@ trait UserAuth $this->Plugins()->RunHook('login.credentials.step-2', array(&$sEmail, &$sPassword)); - if (!\str_contains($sEmail, '@') || !\strlen($sPassword)) { - $this->loginErrorDelay(); - - throw new ClientException(Notifications::InvalidInputArgument); - } - - $this->Logger()->AddSecret($sPassword); - $sLogin = $sEmail; if ($this->Config()->Get('login', 'login_lowercase', true)) { $sLogin = \mb_strtolower($sLogin); } - $this->Plugins()->RunHook('login.credentials', array(&$sEmail, &$sLogin, &$sPassword)); - $this->Logger()->AddSecret($sPassword); + $this->Plugins()->RunHook('login.credentials', array(&$sEmail, &$sLogin, &$sPassword)); + $this->Logger()->AddSecret($sPassword); + } + + /** + * @throws \RainLoop\Exceptions\ClientException + */ + public function LoginProcess(string &$sEmail, string &$sPassword, bool $bSignMe = false, bool $bMainAccount = true): Account + { + $sInputEmail = $sEmail; + + $sLogin = ''; + $this->resolveLoginCredentials($sEmail, $sPassword, $sLogin); + + if (!\str_contains($sEmail, '@') || !\strlen($sPassword)) { + $this->loginErrorDelay(); + throw new ClientException(Notifications::InvalidInputArgument); + } $oAccount = null; try { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php index 75b17796d..1be08fcd5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php @@ -253,6 +253,23 @@ class ActionsAdmin extends Actions )); } + public function DoAdminDomainMatch() : array + { + $sEmail = $this->GetActionParam('username'); + $sPassword = '********'; + $sLogin = ''; + $this->resolveLoginCredentials($sEmail, $sPassword, $sLogin); + $oDomain = \str_contains($sEmail, '@') + ? $this->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true) + : null; + return $this->DefaultResponse(__FUNCTION__, array( + 'email' => $sEmail, + 'login' => $sLogin, + 'domain' => $oDomain, + 'whitelist' => $oDomain ? $oDomain->ValidateWhiteList($sEmail, $sLogin) : null + )); + } + public function DoAdminDomainTest() : array { $this->IsAdminLoggined(); diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsDomains.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsDomains.html index bc2ebd538..fd3b9b555 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsDomains.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsDomains.html @@ -32,3 +32,7 @@ +
    + + +
    From b655e0ad700cfd325c0c04645268259ef7871660 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 22 Nov 2022 09:53:46 +0100 Subject: [PATCH 073/114] Bugfix: .apply() error on leftPanelDisabled.toggle --- dev/Common/Globals.js | 4 ++-- dev/Knoin/AbstractViews.js | 3 ++- dev/View/Admin/Settings/Pane.js | 3 ++- dev/View/User/MailBox/MessageList.js | 3 ++- dev/View/User/Settings/Pane.js | 3 ++- .../app/templates/Views/Admin/AdminPane.html | 2 +- .../templates/Views/User/MailFolderList.html | 4 ++-- .../templates/Views/User/MailMessageList.html | 2 +- .../templates/Views/User/SettingsMenu.html | 4 ++-- .../templates/Views/User/SettingsPane.html | 2 +- vendors/fontastic/fonts/snappymail.woff | Bin 10024 -> 9940 bytes vendors/fontastic/fonts/snappymail.woff2 | Bin 7968 -> 7904 bytes vendors/fontastic/styles.css | 6 ------ 13 files changed, 17 insertions(+), 19 deletions(-) diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 22f4b4e94..e098809c1 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -20,6 +20,7 @@ export const moveAction = ko.observable(false), leftPanelDisabled = ko.observable(false), + toggleLeftPanel = () => leftPanelDisabled(!leftPanelDisabled()), createElement = (name, attr) => { let el = doc.createElement(name); @@ -68,10 +69,9 @@ dropdownVisibility.subscribe(value => { } }); -leftPanelDisabled.toggle = () => leftPanelDisabled(!leftPanelDisabled()); leftPanelDisabled.subscribe(value => { value && moveAction() && moveAction(false); $htmlCL.toggle('rl-left-panel-disabled', value); }); -moveAction.subscribe(value => value && leftPanelDisabled() && leftPanelDisabled(false)); +moveAction.subscribe(value => value && leftPanelDisabled(false)); diff --git a/dev/Knoin/AbstractViews.js b/dev/Knoin/AbstractViews.js index 91be6625e..ac24d66ac 100644 --- a/dev/Knoin/AbstractViews.js +++ b/dev/Knoin/AbstractViews.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { addObservablesTo, addComputablesTo, addSubscribablesTo } from 'External/ko'; -import { keyScope, addShortcut, SettingsGet, leftPanelDisabled, elementById } from 'Common/Globals'; +import { keyScope, addShortcut, SettingsGet, leftPanelDisabled, toggleLeftPanel, elementById } from 'Common/Globals'; import { ViewTypePopup, showScreenPopup } from 'Knoin/Knoin'; import { SaveSettingStatus } from 'Common/Enums'; @@ -97,6 +97,7 @@ export class AbstractViewLeft extends AbstractView { super(templateID, 'left'); this.leftPanelDisabled = leftPanelDisabled; + this.toggleLeftPanel = toggleLeftPanel; } } diff --git a/dev/View/Admin/Settings/Pane.js b/dev/View/Admin/Settings/Pane.js index 207c90440..c19c1d7d7 100644 --- a/dev/View/Admin/Settings/Pane.js +++ b/dev/View/Admin/Settings/Pane.js @@ -2,12 +2,13 @@ import Remote from 'Remote/Admin/Fetch'; import { AbstractViewRight } from 'Knoin/AbstractViews'; -import { leftPanelDisabled } from 'Common/Globals'; +import { leftPanelDisabled, toggleLeftPanel } from 'Common/Globals'; export class PaneSettingsAdminView extends AbstractViewRight { constructor() { super('AdminPane'); this.leftPanelDisabled = leftPanelDisabled; + this.toggleLeftPanel = toggleLeftPanel; } logoutClick() { diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 5de188bda..1ba1d4ee2 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -5,7 +5,7 @@ import { Scope } from 'Common/Enums'; import { ComposeType, FolderType, MessageSetAction } from 'Common/EnumsUser'; -import { leftPanelDisabled, moveAction, +import { leftPanelDisabled, toggleLeftPanel, moveAction, Settings, SettingsCapa, addEventsListeners, addShortcut, registerShortcut, formFieldFocused @@ -83,6 +83,7 @@ export class MailMessageList extends AbstractViewRight { this.isMobile = ThemeStore.isMobile; this.leftPanelDisabled = leftPanelDisabled; + this.toggleLeftPanel = toggleLeftPanel; this.popupVisibility = arePopupsVisible; diff --git a/dev/View/User/Settings/Pane.js b/dev/View/User/Settings/Pane.js index d9d9916f4..0c7c43f2c 100644 --- a/dev/View/User/Settings/Pane.js +++ b/dev/View/User/Settings/Pane.js @@ -1,6 +1,6 @@ import { mailbox } from 'Common/Links'; import { getFolderInboxName } from 'Common/Cache'; -import { leftPanelDisabled } from 'Common/Globals'; +import { leftPanelDisabled, toggleLeftPanel } from 'Common/Globals'; import { MessageUserStore } from 'Stores/User/Message'; import { ThemeStore } from 'Stores/Theme'; @@ -13,6 +13,7 @@ export class SettingsPaneUserView extends AbstractViewRight { this.isMobile = ThemeStore.isMobile; this.leftPanelDisabled = leftPanelDisabled; + this.toggleLeftPanel = toggleLeftPanel; } onShow() { diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminPane.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminPane.html index b261adad7..dc46592eb 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminPane.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminPane.html @@ -1,5 +1,5 @@
    - +

    SnappyMail -

    diff --git a/snappymail/v/0.0.0/app/templates/Views/User/MailFolderList.html b/snappymail/v/0.0.0/app/templates/Views/User/MailFolderList.html index cc360516a..a46b64465 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/MailFolderList.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/MailFolderList.html @@ -24,10 +24,10 @@
    -
    +
    -
    -
    () @@ -149,11 +142,9 @@ name: 'Checkbox', params: { value: smtpSslVerify_peer, - label: 'TAB_SECURITY/LABEL_REQUIRE_VERIFICATION', - inline: true + label: 'TAB_SECURITY/LABEL_REQUIRE_VERIFICATION' } }"> -
    -
      @@ -201,12 +200,11 @@ ▶️
    -
      diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html index 7f1dd2633..45a67e77b 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsSecurity.html @@ -5,7 +5,6 @@
    dragData = null); + element.addEventListener("dragend", () => { + dragData = null; + moveAction(false); + }); } }, diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index ba3852c7b..f3e47823d 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -1,8 +1,8 @@ import { Scope } from 'Common/Enums'; import { Layout, ClientSideKeyNameMessageListSize } from 'Common/EnumsUser'; -import { doc, createElement, leftPanelDisabled, moveAction, Settings, elementById } from 'Common/Globals'; +import { doc, createElement, leftPanelDisabled, Settings, elementById } from 'Common/Globals'; import { pString, pInt } from 'Common/Utils'; -import { setLayoutResizer } from 'Common/UtilsUser'; +import { setLayoutResizer, moveAction } from 'Common/UtilsUser'; import { getFolderFromCacheList, getFolderFullName, getFolderInboxName } from 'Common/Cache'; import { i18n, initOnStartOrLangChange } from 'Common/Translator'; import { SettingsUserStore } from 'Stores/User/Settings'; diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index ac10ada46..1c6b18572 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { Scope } from 'Common/Enums'; -import { moveAction, addShortcut } from 'Common/Globals'; +import { addShortcut } from 'Common/Globals'; import { mailBox, settings } from 'Common/Links'; //import { setFolderHash } from 'Common/Cache'; import { addComputablesTo } from 'External/ko'; @@ -15,7 +15,7 @@ import { MessagelistUserStore } from 'Stores/User/Messagelist'; import { showScreenPopup } from 'Knoin/Knoin'; import { AbstractViewLeft } from 'Knoin/AbstractViews'; -import { showMessageComposer } from 'Common/UtilsUser'; +import { showMessageComposer, moveAction } from 'Common/UtilsUser'; import { FolderCreatePopupView } from 'View/Popup/FolderCreate'; import { ContactsPopupView } from 'View/Popup/Contacts'; import { ComposePopupView } from 'View/Popup/Compose'; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 1ba1d4ee2..69fcbecce 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -5,13 +5,13 @@ import { Scope } from 'Common/Enums'; import { ComposeType, FolderType, MessageSetAction } from 'Common/EnumsUser'; -import { leftPanelDisabled, toggleLeftPanel, moveAction, +import { leftPanelDisabled, toggleLeftPanel, Settings, SettingsCapa, addEventsListeners, addShortcut, registerShortcut, formFieldFocused } from 'Common/Globals'; -import { computedPaginatorHelper, showMessageComposer, populateMessageBody, download } from 'Common/UtilsUser'; +import { computedPaginatorHelper, showMessageComposer, populateMessageBody, download, moveAction } from 'Common/UtilsUser'; import { FileInfo } from 'Common/File'; import { isFullscreen, toggleFullscreen } from 'Common/Fullscreen'; diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index aca6556e9..5bd1910fe 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -15,7 +15,6 @@ import { elementById, leftPanelDisabled, keyScopeReal, - moveAction, Settings, SettingsCapa, fireEvent, @@ -24,7 +23,7 @@ import { } from 'Common/Globals'; import { arrayLength } from 'Common/Utils'; -import { download, mailToHelper, showMessageComposer } from 'Common/UtilsUser'; +import { download, mailToHelper, showMessageComposer, moveAction } from 'Common/UtilsUser'; import { isFullscreen, exitFullscreen, toggleFullscreen } from 'Common/Fullscreen'; import { SMAudio } from 'Common/Audio'; From 7dca36409d548cbaf0fc0d1ffad1791582a22679 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 22 Nov 2022 11:23:55 +0100 Subject: [PATCH 078/114] Show email address for #683 and see what it brings --- dev/View/User/MailBox/MessageView.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 5bd1910fe..4855fccac 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -189,7 +189,8 @@ export class MailMessageView extends AbstractViewRight { this.scrollMessageToTop(); } this.viewHash = message.hash; - this.viewFromShort(message.fromToLine(true, true)); + // TODO: make first param a user setting #683 + this.viewFromShort(message.fromToLine(false, true)); this.viewFromDkimData(message.fromDkimData()); this.viewToShort(message.toToLine(true, true)); } else { From 91978e6ecf9e30053150d7ce7cd481c881871244 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 22 Nov 2022 12:05:21 +0100 Subject: [PATCH 079/114] v2.21.4 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 31 +++++++++++++++++ README.md | 34 ++++++++++--------- dev/Settings/Admin/Domains.js | 2 +- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- .../v/0.0.0/app/localization/en-GB/user.json | 4 +-- .../templates/Views/User/MailMessageView.html | 2 +- tasks/config.js | 1 + 12 files changed, 60 insertions(+), 26 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 9fa455939..fc32c0018 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.21.3" +current = "2.21.4" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index acdfe53dd..bfcbdc35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## 2.21.4 – 2022-11-22 + +### Added +- Added domain matcher test for + [#689](https://github.com/the-djmaze/snappymail/pull/689) +- Download all Attachments of selected Emails + [#361](https://github.com/the-djmaze/snappymail/pull/361) + +### Changed +- Log current shortcuts scope for + [#690](https://github.com/the-djmaze/snappymail/pull/690) +- CSS everything to be box-sizing: border-box; +- Make messageview a bit larger so that it is the same height as the messagelist +- Cleanup and rearrange some fontastic glyphs +- Also show From email address by default + [#683](https://github.com/the-djmaze/snappymail/pull/683) + +### Fixed +- Contact.display() returns [object Object] +- When left panel disabled and drag messages, show it +- Issue with admin domain connection type settings selectbox + [#689](https://github.com/the-djmaze/snappymail/pull/689) +- Mobile View on cellphones: automatic scrolling not working near the visual keyboard + [#686](https://github.com/the-djmaze/snappymail/pull/686) +- Unable to separate runtime from installation + [#685](https://github.com/the-djmaze/snappymail/pull/685) + +### Removed +- Removed inline parameter of checkbox and select components + + ## 2.21.3 – 2022-11-16 ### Added diff --git a/README.md b/README.md index 696b181a6..f9bc9a61e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Copyright (c) 2013 - 2022 RainLoop This fork of RainLoop has the following changes: -* Privacy/GDPR friendly (no: Social, Gravatar, Facebook, Google, Twitter, DropBox, OwnCloud, X-Mailer) +* Privacy/GDPR friendly (no: Social, Gravatar, Facebook, Google, Twitter, DropBox, X-Mailer) * Admin uses password_hash/password_verify * Auth failed attempts written to syslog * Added Fail2ban instructions @@ -57,10 +57,11 @@ This fork of RainLoop has the following changes: * Removed background video support * Removed Sentry (Application Monitoring and Error Tracking Software) * Removed Spyc yaml +* Removed OwnCloud * Replaced gulp-uglify with gulp-terser * CRLF => LF line endings * Embed boot.js and boot.css into index.html -* Ongoing removal of old JavaScript code (things are native these days) +* Removal of old JavaScript code (things are native these days) * Added modified [Squire](https://github.com/neilj/Squire) HTML editor as replacement for CKEditor * Updated [Sabre/VObject](https://github.com/sabre-io/vobject) * Split Admin specific JavaScript code from User code @@ -105,7 +106,6 @@ Supported are: The result is faster and smaller download code (good for mobile networks). * Added dev/prototype.js for some additional features -* boot.js without webpack overhead * Modified Jua.js to be without jQuery * Replaced Autolinker with simple https/email detection * Replaced momentToNode with proper HTML5 `
    From 5069bc29507823382350dff92badb954b1af18eb Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 22 Nov 2022 23:54:32 +0100 Subject: [PATCH 085/114] Bigchange for contact image avatar support #115 --- plugins/avatars/avatars.js | 19 ++++ .../avatars/images/services/amazon.com.png | Bin 18032 -> 3498 bytes plugins/avatars/images/services/apple.com.png | Bin 2461 -> 1150 bytes .../images/services/cnet.online.com.png | Bin 6623 -> 0 bytes .../images/services/connect.asana.com.png | Bin 995 -> 0 bytes .../avatars/images/services/e.paypal.com.png | Bin 4545 -> 0 bytes plugins/avatars/images/services/em.ea.com.png | Bin 6157 -> 0 bytes .../images/services/email.blizzard.com.png | Bin 3194 -> 0 bytes .../images/services/email.microsoft.com.png | Bin 8348 -> 0 bytes .../images/services/email.skype.com.png | Bin 3435 -> 0 bytes .../images/services/facebookmail.com.png | Bin 10088 -> 0 bytes .../avatars/images/services/google.com.png | Bin 5949 -> 2196 bytes .../avatars/images/services/id.apple.com.png | Bin 2461 -> 0 bytes .../images/services/intl.paypal.com.png | Bin 4545 -> 0 bytes .../avatars/images/services/microsoft.com.png | Bin 8348 -> 382 bytes .../images/services/microsoftonline.com.png | Bin 8348 -> 0 bytes .../avatars/images/services/myspace.com.png | Bin 7553 -> 0 bytes .../images/services/news.myspace.com.png | Bin 7553 -> 0 bytes .../images/services/news.onlive.com.png | Bin 5000 -> 0 bytes .../images/services/plus.google.com.png | Bin 2952 -> 0 bytes .../services/postmaster.twitter.com.png | Bin 1104 -> 0 bytes .../images/services/reply.ebay.com.png | Bin 4621 -> 0 bytes .../images/services/reply1.ebay.com.png | Bin 4621 -> 0 bytes .../images/services/reply2.ebay.com.png | Bin 4621 -> 0 bytes .../images/services/reply3.ebay.com.png | Bin 4621 -> 0 bytes plugins/avatars/index.php | 91 ++++++++++++------ plugins/kolab/KolabAddressBook.php | 6 ++ plugins/kolab/index.php | 2 +- .../RainLoop/Providers/AddressBook.php | 5 + .../AddressBook/AddressBookInterface.php | 2 + .../Providers/AddressBook/PdoAddressBook.php | 34 +++++++ .../v/0.0.0/app/libraries/snappymail/dns.php | 6 +- 32 files changed, 134 insertions(+), 31 deletions(-) delete mode 100644 plugins/avatars/images/services/cnet.online.com.png delete mode 100644 plugins/avatars/images/services/connect.asana.com.png delete mode 100644 plugins/avatars/images/services/e.paypal.com.png delete mode 100644 plugins/avatars/images/services/em.ea.com.png delete mode 100644 plugins/avatars/images/services/email.blizzard.com.png delete mode 100644 plugins/avatars/images/services/email.microsoft.com.png delete mode 100644 plugins/avatars/images/services/email.skype.com.png delete mode 100644 plugins/avatars/images/services/facebookmail.com.png delete mode 100644 plugins/avatars/images/services/id.apple.com.png delete mode 100644 plugins/avatars/images/services/intl.paypal.com.png delete mode 100644 plugins/avatars/images/services/microsoftonline.com.png delete mode 100644 plugins/avatars/images/services/myspace.com.png delete mode 100644 plugins/avatars/images/services/news.myspace.com.png delete mode 100644 plugins/avatars/images/services/news.onlive.com.png delete mode 100644 plugins/avatars/images/services/plus.google.com.png delete mode 100644 plugins/avatars/images/services/postmaster.twitter.com.png delete mode 100644 plugins/avatars/images/services/reply.ebay.com.png delete mode 100644 plugins/avatars/images/services/reply1.ebay.com.png delete mode 100644 plugins/avatars/images/services/reply2.ebay.com.png delete mode 100644 plugins/avatars/images/services/reply3.ebay.com.png diff --git a/plugins/avatars/avatars.js b/plugins/avatars/avatars.js index 6d45dc376..f18dbdf7f 100644 --- a/plugins/avatars/avatars.js +++ b/plugins/avatars/avatars.js @@ -22,10 +22,12 @@ view.viewUserPicVisible = ko.observable(false); view.message.subscribe(msg => { + view.viewUserPicVisible(false); if (msg) { let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0; // view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`); +// view.viewUserPicVisible(true); rl.pluginRemoteRequest((iError, data) => { if (!iError && data?.Result.type) { view.viewUserPic(`data:${data.Result.type};base64,${data.Result.data}`); @@ -38,6 +40,23 @@ } }); } +/* + if ('MailMessageList' === e.detail.viewModelTemplateID) { + const + template = document.getElementById('MailMessageList' ), + messageCheckbox = template.content.querySelector('.messageCheckbox'); + messageCheckbox.dataset.bind = 'attr:{style:$root.viewUserPic($data)}'; + e.detail.viewUserPic = msg => { + let from = msg.from[0], + bimi = 'pass' == from.dkimStatus ? 1 : 0; + return `background:no-repeat url("?Avatar/${bimi}/${encodeURIComponent(from.email)}") center / contain`; + return `background:no-repeat url("?Avatar/${bimi}/${encodeURIComponent(from.email)}") right / 32px;width:68px`; + }; + .checkboxMessage { + background: #000; + } + } +*/ }); })(window.rl); diff --git a/plugins/avatars/images/services/amazon.com.png b/plugins/avatars/images/services/amazon.com.png index 07ad04dffd215534a87c774774749e5e9d12c845..adfc6f5267b6ac93903774c9d96d6b0a492cb2a5 100644 GIT binary patch literal 3498 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 literal 18032 zcmXt=1yEaE*M)I+cXucaTCBJ`!QEYoySuwP6e-2sU5iVx;!xbJIQ;khW_}owkU&W8 zJ!kK=p0$T4Wko4eBqAgT2nbXeX>l-c9r)iD0vvETC}=zZT)~;hNr^*z{P$VdU7iBm zgXk!&?E(RTjQ!siBt&*D0dOb0tBisK{5lF0EIq23gP{Th1Ovr_5&ZLN3b<>lR9*Q%N~Z%_QgJDH9~CU}Wx5LCy493+eq5V+K|c78B+bi^*4 zO+!$I7$~bc&ndwO8;D_Y#Y{vA1C}jGMWBpchWkbnW!ki)l^(W~4MJ)3sk8U?C!c7j z`$SxdyWz0RlqOqmziPz^6n=7|=H0iuDhPNoPIwvAircw|W{6ITg6{zJh2a>f${ zJkhcnCPFf2Kj-8j%=U3CjZ8G`7X|FK7R~O9`{z{VG&{&*1WK4P>tq_laO1j$2IsIt zFU{Jjs=BT&`PMaBgkVwZzd8~KDpq*QZcZfd6Add3=EzePs=rU}MJaB4Fu}<`aZIJ# zoQwEaEWOi4o>Yj-4WUU6FAqDvJs!H&y(H5VX;den;ngpno^vX=Jmh(7R($I`=RP<% zxbiDdEFWB7CuwYKWMO5UKX;c=_fFklsI$)jSBSUkjkMTL^B$74*sqNY4Z)v)U z=zTuO`F8NLvc|;19WKzjTCqaS#M&Bht;I<{16&bmzZUjq=G;4(X2_|m2U31vsDVX( z!OY1iX+O;zLh^*#m5-D&8`;1U@>{^m+Eduaa|~yWLK3n~wT7HpD9f!k{&N zU^BKWC*-vNdMQq`4k{z9F~LnAH2RSgnOCUL$KPgwt3K$v7eA&nBy4Q##~vkuaCv!o zjBw+fzlfB|vbYws3aU2EMAq4OLg+crjI<&c%B⪙A9$^gR52dB6V=}W18U;?&iOi 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 b1_u5DZZJDCvVAz{00000NkvXXu0mjfrX--& diff --git a/plugins/avatars/images/services/cnet.online.com.png b/plugins/avatars/images/services/cnet.online.com.png deleted file mode 100644 index b3f2b53252441b0e61a5b5fbd24e9af865838755..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6623 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 tzVksD$ECQVg5%%FEdSG#Cv zwc+#Gk<)em|Nly(-aoPqrqb_Yui}Hi=;ZPGI-A=el-Azy)za<8_xt{Y&1A^q^_|V_ zX2CzQ;GXRF=lT5ip4f$DvE#GY@j;&4-tPHQt=@dR=V8A&jm7H4;q>|Z{yMJ*ddXNy zwj8_Us-w{E*YC}U&uOaNlq!?h^ZM{w(EZ*psMAVX6$ z2C~zj0007FNklRH%?a6N zfIt;kf$f@r$4t*c*J=RaH*UTlM8R=jSx&d~jgF!y9iuJlQ6ad;fXO!7?t}BKcza=L z_1}}xR~iU08OB;JB|lZUiC>~9PH!QgkU<;fRk~7kPA-o>i&>mPK+eYWIQMCgn=22W z2rz|!o{ed70GCulSR9@b;0*v0I0qF00m_10f5?C{2=qM9n{e_?9Rg_n^Wppi!CN>G zO5Ps0+7iR^*D=7I75#zPJ;epgmbST<^-4?C1x#(?FI7j11Q_)I*Y09YKh*&`oQ71T zT7bpgPW0XaVF;|Rr%&@epiO5=jw-h|SsRBCxVTE|=yFa0ewp~u8T&?{^>;Ix5cp0? z`3nS;Vp_QaHv|U2Y^J>0Bs*a(6b0N6;0FM#e7*K3X92^zH2>WN0LThNoO~KU;mhFd zD*^!7QCFON`YC|e0^JPYZAgf&0|r2@pv%iQj+b)p&>HwPdSR141=d4U^g7LCO8Fpz z8kNLlgH(ax`&$Y?b}`JGXi2{RCX3lvVz%@1gLpNf095JZ2s`Nq2BvnD9dJe}@tFe5 z121RY0l+l@?kFJbXL=JW(hU>?U_gNLJplE4``a0ovzMxw+EEES65wVZNNd(=ubg7x zB4>QR*uKw#*(}}-8(oUxfL_F1B(7!!L7?&8lxeIVKNUa!#}Ca0Xn=nK{062G^tsKJ Rrx^eM002ovPDHLkV1h^d?aBZE diff --git a/plugins/avatars/images/services/e.paypal.com.png b/plugins/avatars/images/services/e.paypal.com.png deleted file mode 100644 index 7ddea03c9940de970baf32efa1ecc986a101b6c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4545 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 diff --git a/plugins/avatars/images/services/email.blizzard.com.png b/plugins/avatars/images/services/email.blizzard.com.png deleted file mode 100644 index f5baf8db852946a1c01959356339aa4a65c8b473..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3194 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 N002ovPDHLkV1ju=ilqPm diff --git a/plugins/avatars/images/services/facebookmail.com.png b/plugins/avatars/images/services/facebookmail.com.png deleted file mode 100644 index 2d416f898d2aae613c9fc79f0897ce880393f980..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10088 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 LMNUMnLIPldr;AJQ literal 5949 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 b1_u5DZZJDCvVAz{00000NkvXXu0mjfrX--& diff --git a/plugins/avatars/images/services/intl.paypal.com.png b/plugins/avatars/images/services/intl.paypal.com.png deleted file mode 100644 index 7ddea03c9940de970baf32efa1ecc986a101b6c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4545 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%0Dr`{1U9kWhL(H8V?>rg_ zfpvKj&5r?BH{Wm^4%76=Cj(%~SMP-{DE{8T!Qsn~?(ar6s`FX_s1X7|@~-Y>YAxW$ zv3;S^fEhf|Mq)S`S$TQ%@Twk0{wsj7d&1D%e8G3pAc2?wK)1~Pe*7!x&tNLwlg{}g z3FEw;AOb}QK(@JoHM0gbha{)cPTnHna?l%g_ruohbp!j{Z7wXi(rrg(e)Ggr((DaKN$)3a16ML;e&|wta5a+iE*)s ziFp=d!(XR61G(Ttc=@PslI?Co0JR@W?*PDHHN4&eRDEW>qxqzq90@WoM@PVObY%3X zz&wV4IU;|K!>^Exod75uKJUIbDnkVyxcv>htgoan9UPF2vwpoq3;5f(y3E(a46hn# zw>{&((!AoYV=y!f#*mF2_z3*#u^S7)cos3Ymo6Jo(rT~EYaO&R7zZFTU_?8bo8O%P zjKl!=8FI3V9$ExK!02%r()9G$F|-?wk*_lY&Cj>?{_lcYFml+pUEznMi`z1!os87f zO`8Cj`4_(XyG$ko0q^-eV2QpSz0-#OYS;z|tV+I$54MFJ82dUKcl}bKy!GwgD_3(R zk}t1@uhyQ`-L{PZE^h*=jrrTZ>Rn#G`0ckBFOj0)GByL%D!wEC4Gkk7%)#y%-)UVK zW0eiRK1`$1t3^M(x$D0_Dc!KG^ou1UKUF>5!DLFMz(0NKR%B@?i--AI8v>)Xiy3}7 zc4su-iNP3+{kmZgXg=SjXUzWuK>kyrm*0i8%Rc=!b3{hj+KvG(F467)O3%x6&;I=9 zXX`X}a@9+hn`3uAiFu(J|H0GF-NnUy(Rhe;iXAf;qUS#vKZqm?m`oB0JToc2#Ulwi z*r)1&=pfQP2BeT&TTxZhJ`Z3W1xOeNkR}UTSlcI7$tRa;)obgb|A75lfB^svE1tgi SJ+A2h0000dg7KX~>BtaZKSP9OAJA@o;QQJR17C3hDecmfv%t$M2bF+QtP^ zNi5|7&52s`E2&|%@~VlZYusV_6dG15fi96kf}UuS#urjZ1iD0NAq<^i^)gV1HSZ|Q znI%{y^YP9uMOYRpuK-U`@u(8j^3oGDKlT739(C2;)|w?Rd^(_h$9I=v2r*=TErg*4 zTe=Js5;*H3^1gZozBvnA5i=6RI@_qcXDyMf@8eg}74ZOZXYh@SI~v1q!2m!VzWGM+ zEQ(hDoT;atJ!pqSIE3=~T@mLNO`G21zjxn`8EnU|Bm#Gf2e8u!Zo2vb(4aILtt^Tz zyN1H^Rw3k}YcSpuq~_(PY1sH0!K$4IKq!-(c?&2gTgLoVSKyfva82;xa1wDU|8_qO z&p%48GVmz_L-sCz7z;>S?_vy%M)?+(G4Hl=yt7MFe%8P9Dpe1aBf8qKq@c*F5C}9H zyXFg8_U@wL)n}P~$>l6qbw$RB!c7PG_{JX+X{ z4f}y41C&UPo$bxSXjTgJ~Xsy#0f$n95p!MY^Y2NZ-#tHqU3z_z#8?c@9V-B|rx-hr>0eSc!t)Fc|G}K@j z0_k#V1mKa9T+^WHzIDWVf*IRSKYtaTg~v_~yG!d-326P`Exd;Nd$S6z*hX7|Pu1CBtXrYdV~YzTqZkTkvgBz82Cv29@Kazx?iT9Mgcz!69^)+afl zvnvXL(b>j<4X+G2dETjK;W*jPmGQ23*;i{#sv8&y56cqk4=KQug0>IeV%E9e%h=Yp z;21izMjj?{w$>c;?}69aaPgH_w!3bdA^}1v5So#EN<#P6_lXBPGfwQEw}3>yzA?wi zdePY;g#m_t%7{f$A}}?y3~?n|g)?v*ozmAH|MjObK;=(kq{=yIC_!k?u8i#rpMS)2 zjRsO~0pHyDWQq;Ag&_o?>dXvq67fl43#h{-z+bwEE(b;$7nMM?wmM^*M0XI;Pm>$K zhBKBBaj6R&tvUbgfsB56#iA7DnhwFPQ0grZC|gE7{jY}X;B>ZS{2ys)I;i~mU`hyh zB)Qt%4n)%^jeu!jx!FY}QzgJVeI}lg`RKYmBXUxN03DCwB;rVA4%#MIwPS#}Err1A zB9b!{EJM2T2D^kvRe}Kdr>{i!)6T;!LW9r(ZQE%lZ{PG@(&m^*LkI>kpyM1K|-}oxwFDi&s?;={a56=-LsKV*wE&mn;r=5fBckOl8 z)v)>2tB?n3$W_LKM}Y@IBVND$3jK|BcVSH}7}cS>_U&f#^*_Ms>BMiy5yk)0A!0jp z#O)N5*fJzjO#`nBQ+QuX7slCZxRLDkP!#dp3A6Off;_^uLri%nT)u+BrOTLh!YSm=nv)e_+kSf^ z;rHJlXBaQN6M^dHPLvb^pA?uv5_jT+-g}LXjjysxYitJ$pAY}+c>shO>c~ItWKRD5 z18D~hHy)tt{WqB+4?V0*R%k$JlA`1h0#gdST9DAm``po>ofyu(N&s?!c`GlYmFR=PC-&a zqyT(=q}P|xzV^>4#ap^?QiQvPLa#UIzJn8q;MjI%=R`{$)9$Y^aXJ5Jvp{b z_rBdj4xGF27)tK?Da#bKSdz^P|4YyBh0K;h`Q!XqqN_dWp(rUx`&l^%_! zWG4LP6AcMh)MX4)aLv-s06UKxc&7FvE8AUUPE9>i4w{8Izxa!AOwRF zsY!Z|5nI!3JLD`ojm0krerKJJgpw>gx?h#s2dyo3Flv&M$Vlx*|PCl;Hp71>F4j= zF)z=LB~x>PvqhgYC?PPkL({u&5{oCuFFPq?Kb1R$j*s4ku8wij1r0qBq)#5o3m#1? z%Rt~0l00S5`plzLK3G1a6nKw22|XIQgKi5+9XDnimjWy)$ukXtuRl-Si%(``fKfCP zXB;)bu-)No(5Dx24N2oaA0kv=o8ECUt*jqpvw=n#GL6HcU0Y8nklkqoQpL}I}1w|@LC zvL`sAWNkMHCv57v!!-1Sw>Z&wxw@*lz4PhCENcu+U(*;0Uo3uit!0000(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ<)=5M`RCwC#+G~iEWfTYS-`HB_7S(oBvUbQY7l~tu%y&t zzb1ZOf#qmTF{wOP1Bo0?#2I-cHew!za&ycllEImIpJ#_#C#OK96IZ2RCmY`B#1uS& z=LS9rJFptJU~P>r&)}YvJUbZnx^PPT?!sao$rKqp4Xf~Ao@=YY$v7t^BI9v> zkoX&O!bATd(vNK=3>=8ZBENT3`SKY>l1jbkkMMf8*kaFOT^R!x;A6ZI_m$w7lu3LS zh4k&=T9kJxG&UD&q9)uNd4AaHn<5q#Td##LSrLq^a?D)JLoAE35kFd?8H0Nx&?l+f_-ij8Y9vq6(!dn-mNCNuG5ji@t zz^!-|Pv-iVQHHJMLTd-k!_{%Wu}Ih1Z0y38avs@h(50^zt$w33L zTYR`w0UBZKj6FzAy4J*K-DkwQ+1zAr{9H}1m5(`?kKb`X)RtyoH=d|O6exuca2RId zEF9buVX@QpIyKCDQ5j(i69HSQ)mr)~ljS!XEr7ktJIt;&r=3TkQxj zA#Lo5HfTXKtLh~(JsQMz$%)dqP(m7c#@LOA)Gm$}T_dX_8!d_j-PS-7U*aao+2Pu> zjOI_a506I(-57D3)`?XgUXuuT=i+50gtmG^DUenY-wZ#Etj>vk85!xfhj>|YJ^`dd+m+M_r|`Zr_mEHL}u(uHDg4^i2Of@{4)TT WU&sEBduajy000000001b5ch_0Itp) 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 D$PlZ| diff --git a/plugins/avatars/images/services/reply1.ebay.com.png b/plugins/avatars/images/services/reply1.ebay.com.png deleted file mode 100644 index b38920ca3eaf7547f9388e0d244257b19a67d3a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4621 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 D$PlZ| diff --git a/plugins/avatars/images/services/reply2.ebay.com.png b/plugins/avatars/images/services/reply2.ebay.com.png deleted file mode 100644 index b38920ca3eaf7547f9388e0d244257b19a67d3a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4621 zcmV+o67ubdP)00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf5&!@T5&_cPe*6Fc02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv000quNkl#A$qsB7<{iG`02U%B`D zpLgc+f#r9jtH0+z$M?(`=H8h*^FH(T4`%v*POkyHYI+UeRnuz#ubN&1crwZA-zSoy z{v}e!PhJ01dS(DMPX{Rqy%7EHA-)pq636CmtEhS`%d$Fp!p_xbG5Z`6c$V9G`1D73 zF+V=erFg;lr_tjJzy`fk{tFdS6eHjNQBtSP^!U-7WX>K21(gr3mt4MGcH?%*)%?=y z=6196`rV4#d`kt~imz(!CYy+X=LcYvO$MWaX5>9@>Q#x}Ob;E(X8~T84YE;>xu5SH z?3CrwHP<&h*EcN3$J~czd-vSl2EfE8{PSw!o6yVW1VA5G@5z-4mqb{&WFI2BJ#iZG zZLB2G@dZ<_Al*jZVsae{tUMgq%rh_UR4gJ2s!ELL%-7G%b*Ig-Xd~rSO#T!EsaB z&EvN+JEXgH+2Iws%PS<;JvhfBIL9qG+pB$+Q~zBZOLZmu^Xfgt7=-aN0|0K$r93tv zhxtvh>^P4F&XI!VvNkhZ-&t0vGhmInca_5Mo^D;%6`0O1G3^aivYRSfE#xf{nlVK( zW!E1)-O*#BM~7wZp_%T%J3KmOdjddcu20+a#u?W)Vh6Jj$RKDtI_Tit-Or;!P zzuGBvh^yUmu_kh%W%q?FY=Ot{Rb~Kup^8M+nY(*T*^crLlDvr-lbO7Q0fUliY20FI zN@Kg1*R~zt>>B0d`+-Zx^_pt6LG$b3QL zmhO-OXIn-lvWP^Bu1T7Zc-Ns{1AuvV5FZ+8P;gw?Vhz?!TdkBW7D|dnPSq$InfwDa zV+u2+OVOV-wj1p1GT6~$h->E!o}qc}owMCTay|c;>2T=YUNC@L7dyiN;PF(!Kig+H zKLUWW_Edrj2?+oI3z}`!Es1p{08((g>yXu40%oNb@rWzWzV6_j?mI*j-Y8?{7s=7_0T|I_7JH6}FBGxrD&jSG9i(ubVkB#9+8$^IJ$d!1U{m)OK zV9#U3!cqkQl0er)DFOiI3khIK_y;1+y;6KZNv$QPGF&{kC(Xg#pG%^7Sw{=5{?)h< z!qG&qpZBIPq%>(cC4(^B-+^x00ud=Tj2qx1Qx?` zyr{`c>m@*pnT=dJdHffD8#YiX=RgBb(diZJd3Xd{s1HV{Mm7L(?M*wKLR7*go z=N$8_#%Sal!ep+jGlA)h`bydxN?JWAk}2Cksk}=oNt>bDrE-o3SxA~@;HOPs39#m% z#oi%1$qaD)9kN||?PyzAp~WYlU(53&^I9NQV2TNzon%csPbW$uLWja#{gwV!Jhk;u9vm~8;qZa*md_^Tc6F{|tKUQ)YfqFw*5!oT=o z`_T)4As1gVotm!f3NU3hgfS?a8M&s-l4%qCl4RJ*3|q*bZd*~$<{c*hP*o_~t5c3g zXpSd@aQyBe#I=@N8`AY>aRAx|zD2gpwRX*mnzqq8v<0IW(_`b-h0WTa(b%>nmKD_V zcwprURQD~NOf5s_w;N#U37i!c0x4_!83H1I97BnbdsVIzMuj?=Huu2q3uOI>}h zZ#6E_l3yF2;jG&tGK0|grcuj1hpb{9;)F~Pp@GdJBA#{q%lpH+9U05C-$+Wj6@@VJ zr`3T>`8#nh;sXgGlVvWf-73xx00ft40tvDs7_@j4jj5bjyO#8h(QQgIoII;s&o^AZ zWjuAZGGlw`sLu-Ca4)p+DQxHmoC3)Mo8~$@|21`*4`ROKm`i~BVAgF33tylKPtbIn zXW2Euamd$oA{VmG^Q4FbDQu~C@6V1+lz#eHb840bDsD=qYc_7SP=4xWIyFtXutd4E z64{Xu3#Aw-3}Io+8i&9<&yZ~7ry{*mF^BKl910t?xNqZYu=QVM?Kg<|kI?v!VIHn;PLR4Uh7jxrE47DkSJTB2MS}Fj#O=%P_g5lsMLvSGoU63JbbQMX$pFB|Qd{mD{Np1c229z;~47 z|2}i?V-pHvfKytQaun-DG}wFcDj7hRMeD{oADh7T$4dJ?Vt3bQNCF3WekNB^B$f^2 zMymjjE~Q$MYMq%m`pB?=bUUPU!sVMAOad5S)&Ut!8H}=Ycr0yKef?@8fJcDA0P?{y zX#W!cXnaRXKBG0wZ8}b8T@p`RxQ+F~&z4l^9izWyVY4$nX>cS~0FZ7hwIBdzX4C%Q zo}WmkiWb69!9#q8sl&AWwhwmV7Or_j~|j zxO`RCYRf0ZX)Wvv8@H$lU{x(&02s~uV;E_XTZ{45{Z{^VpNbEP+Y|`pz>hc93Yq+4 zY@I`~>_9BrGfujc!~vXN$^js|B&?_tm>a~G-%R7_#f2^q4785UaOP`t*DVNj2HmcTKmHW0ARS}H;#FFd5y9e z_H9XNIXFYfCaSiGNn2RwX?YXs1AtJWbMr0W^^K=zfN!osJa^KbAO}j>9&a3g@$_^| zZNHKHW`-Tu1>b}r8zmc!VZK2b4oB}EBrim6w1MyA4cH3);6vkv-dLlK5fBhsfV*2v)0lM?6*^o!6a#h)7W>cGieR_-<7USuX!gfi5P&#K{=B=#nGCyCRITb zkD@hhQDE!OBO1jx!~uQN7T?zP8_ayhNWP;b-%psEyZi8FBmb)7*(eAA&_~Tq)dVFi z{;GOjF+WxUl?WZ;in&cSx zxu*S0mzXu?bXwSP)T|U1kn~RUkY7HR_Qq-e;Pe~{w8ZXa$(ZY=WI2#aO}4~9#4=cj zaVrP#%NGK`d;5A)=*{u2DFKu+la74=X)~liW$%5?S5{Rag6Qj-e^tVxn#`@(&4L$O z_F2Jtt(1DNU?B_41&vT43*w>ZAb*j9GEgPr<>q7PxyPONI?h-q%yy-Kd{> zlMM(7a2P@Y0DvPXnS-l=p3qvIPW)acV2B`KypfM=w171gDJt3nX0Ma%*D>c|%yk@d zAH&?nSbEoLH?8rzUkfh;*uo2SpS%f1Y#F%#358>?DPP94JvO(2DT8Z#+8gqYR=9X7 z%0LCjO$YiY2cwk31C>LAXnbn2kN^NYqC&8T8a4oXwQw#SJrNJIQB4x5MEqBfQjz_e zTHBT|X!H*nlMy2qf)^TL(Q*uP332MnA{IC3v-oZSW&b2TUKV$r@I7)nWOc~)EF%!A z6Gz1-o0J>If>XwdTO?^Z6j#QT@?jDzV8B&K1t3yrvjn&eRHdS&(c|Q?!VV+|q3##K_Xr$mc7MVm-sLN2bwVN~W%M^S_K3a_~PlKUK zKrwytkLbv}7H{ti=)L_dv|c;jdV6P3c&_i(Xvuq zagnZ|Sg$SC6{$_H#hY?N85S`(d!vD@UhFGA;_UKhAb#`WRgO8#8Te0&;^%dX4slZ8 z9Q4d*a_o}4{ikn9p$SyQhsKjYfFxuAaH6fu`Wav0`uQ0T0eIxGN;3Q^G4;sg!$cCG zMe4Sqz!RkIt3Ff_#==Xk&HY!und!q5vWCTHq3<^{ZEem;ygza2x2V}^4pA#094MG= zzh#ks+O+p)`cDx&CAg`%+Y{-b<5UKO%c@DL`+DZzMjyWqI)fjBG5r&U56a5*SAW%B z%r7b^p`sMtT~+s99a!ss~x z2ofAVLgx2mF(B|?^^$+|g6Y2k@V^n~UuCxj*TOVkFufE2|CjWF0aRW5OS$YP(u)J| zQZ)QO(@R#NmyhbT4ZLc44d7MNYXGmBUITcwWHS8^WiW!0h;Ok&00000NkvXXu0mjf D$PlZ| diff --git a/plugins/avatars/images/services/reply3.ebay.com.png b/plugins/avatars/images/services/reply3.ebay.com.png deleted file mode 100644 index b38920ca3eaf7547f9388e0d244257b19a67d3a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4621 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 D$PlZ| diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index 038e21137..61a8547c4 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -8,7 +8,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin URL = 'https://snappymail.eu/', VERSION = '1.0', RELEASE = '2022-11-11', - REQUIRED = '2.21.0', + REQUIRED = '2.22.0', CATEGORY = 'Contacts', LICENSE = 'MIT', DESCRIPTION = ''; @@ -53,48 +53,82 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin return null; } -// $this->verifyCacheByKey($sEmail); + $oActions = \RainLoop\Api::Actions(); + $oActions->verifyCacheByKey($sEmail); - // DATA_IMAGE_USER_DOT_PIC - $sDomain = \explode('@', $sEmail); - $sDomain = \array_pop($sDomain); - - $BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null; - // TODO: process $BIMI value + $aResult = null; // TODO: lookup contacts vCard - - // TODO: make this optional - $aResult = static::Gravatar($sEmail); - - if (!$aResult && \file_exists(__DIR__ . '/images/services/'.$sDomain.'.png')) { - $aResult = [ - 'image/png', - \file_get_contents(__DIR__ . '/images/services/'.$sDomain.'.png') - ]; + $oAccount = $oActions->getAccountFromToken(); + if ($oAccount) { + $oAddressBookProvider = $oActions->AddressBookProvider($oAccount); + if ($oAddressBookProvider) { + $oContact = $oAddressBookProvider->GetContactByEmail($sEmail); + if ($oContact && $oContact->vCard && $oContact->vCard['PHOTO']) { + $aResult = [ + 'text/vcard', + $oContact->vCard + ]; + } + } } + if (!$aResult) { - $aResult = [ - 'image/png', - \file_get_contents(__DIR__.'/images/empty-contact.png') - ]; + $sDomain = \explode('@', $sEmail); + $sDomain = \array_pop($sDomain); + + $aUrls = []; + + $BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null; + if ($BIMI) { + $aUrls[] = $BIMI; +// $aResult = ['text/uri-list', $BIMI]; + \SnappyMail\Log::debug('Avatar', "BIMI {$sDomain} for {$sUrl}"); + } else { + \SnappyMail\Log::notice('Avatar', "BIMI 404 for {$sDomain}"); + } + + // TODO: make Gravatar optional + $sAsciiEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); + $aUrls[] = 'http://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404'; + + foreach ($aUrls as $sUrl) { + if ($aResult = static::getUrl($sUrl)) { + break; + } + } } -// $this->cacheByKey($sEmail); + if (!$aResult) { + $aServices = [ + "services/{$sDomain}", + 'services/' . \preg_replace('/^.+\\.([^.]+\\.[^.]+)$/D', '$1', $sDomain), + 'empty-contact' // DATA_IMAGE_USER_DOT_PIC + ]; + foreach ($aServices as $service) { + if (\file_exists(__DIR__ . "/images/{$service}.png")) { + $aResult = [ + 'image/png', + \file_get_contents(__DIR__ . "/images/{$service}.png") + ]; + break; + } + } + } + + $oActions->cacheByKey($sEmail); return $aResult; } - private static function Gravatar(string $sEmail) : ?array + private static function getUrl(string $sUrl) : ?array { - $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); - $sGravatarUrl = 'http://gravatar.com/avatar/'.\md5(\strtolower($sEmail)).'?s=80&d=404'; $oHTTP = \SnappyMail\HTTP\Request::factory(/*'socket' or 'curl'*/); $oHTTP->proxy = \RainLoop\Api::Config()->Get('labs', 'curl_proxy', ''); $oHTTP->proxy_auth = \RainLoop\Api::Config()->Get('labs', 'curl_proxy_auth', ''); $oHTTP->max_response_kb = 0; $oHTTP->timeout = 15; // timeout in seconds. - $oResponse = $oHTTP->doRequest('GET', $sGravatarUrl); + $oResponse = $oHTTP->doRequest('GET', $sUrl); if ($oResponse) { if (200 === $oResponse->status && \str_starts_with($oResponse->getHeader('content-type'), 'image/')) { return [ @@ -102,11 +136,10 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $oResponse->body ]; } - \SnappyMail\Log::notice('Gravatar', "error {$oResponse->status} for {$sGravatarUrl}"); + \SnappyMail\Log::notice('Avatar', "error {$oResponse->status} for {$sUrl}"); } else { - \SnappyMail\Log::warning('Gravatar', "failed for {$sGravatarUrl}"); + \SnappyMail\Log::warning('Avatar', "failed for {$sUrl}"); } return null; } - } diff --git a/plugins/kolab/KolabAddressBook.php b/plugins/kolab/KolabAddressBook.php index 64c058e09..89b704227 100644 --- a/plugins/kolab/KolabAddressBook.php +++ b/plugins/kolab/KolabAddressBook.php @@ -313,6 +313,12 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt return $aResult; } + public function GetContactByEmail(string $sEmail) : ?Contact; + { + // TODO + return null; + } + public function GetContactByID($mID, bool $bIsStrID = false) : ?Contact { if ($bIsStrID) { diff --git a/plugins/kolab/index.php b/plugins/kolab/index.php index 84902031a..49cb56167 100644 --- a/plugins/kolab/index.php +++ b/plugins/kolab/index.php @@ -8,7 +8,7 @@ class KolabPlugin extends \RainLoop\Plugins\AbstractPlugin RELEASE = '2022-09-06', CATEGORY = 'Contacts', DESCRIPTION = 'Use an Address Book of Kolab.', - REQUIRED = '2.18.0'; + REQUIRED = '2.22.0'; public function Init() : void { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php index d9f0fd987..6136e07c5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php @@ -60,6 +60,11 @@ class AddressBook extends AbstractProvider ) : array(); } + public function GetContactByEmail(string $sEmail) : ?AddressBook\Classes\Contact + { + return $this->IsActive() ? $this->oDriver->GetContactByEmail($sEmail) : null; + } + public function GetContactByID($mID, bool $bIsStrID = false) : ?AddressBook\Classes\Contact { return $this->IsActive() ? $this->oDriver->GetContactByID($mID, $bIsStrID) : null; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/AddressBookInterface.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/AddressBookInterface.php index d0ce8ef54..564a0d48b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/AddressBookInterface.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/AddressBookInterface.php @@ -20,6 +20,8 @@ interface AddressBookInterface public function GetContacts(int $iOffset = 0, int $iLimit = 20, string $sSearch = '', int &$iResultCount = 0) : array; + public function GetContactByEmail(string $sEmail) : ?Classes\Contact; + public function GetContactByID($mID, bool $bIsStrID = false) : ?Classes\Contact; public function GetSuggestions(string $sSearch, int $iLimit = 20) : array; diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index ae698febb..f7bbf15d4 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -694,6 +694,40 @@ class PdoAddressBook return []; } + /** + * @param mixed $mID + */ + public function GetContactByEmail(string $sEmail) : ?Contact + { + $sLowerSearch = $this->specialConvertSearchValueLower($sEmail); + + $sSql = 'SELECT + DISTINCT id_contact + FROM rainloop_ab_properties + WHERE id_user = :id_user + AND prop_type = '.PropertyType::JCARD.' + AND ('. + 'prop_value LIKE :search ESCAPE \'=\'' + . (\strlen($sLowerSearch) ? ' OR (prop_value_lower <> \'\' AND prop_value_lower LIKE :search_lower ESCAPE \'=\')' : ''). + ')'; + $aParams = array( + ':id_user' => array($this->iUserID, \PDO::PARAM_INT), + ':search' => array($this->specialConvertSearchValue($sEmail, '='), \PDO::PARAM_STR) + ); + if (\strlen($sLowerSearch)) { + $aParams[':search_lower'] = array($sLowerSearch, \PDO::PARAM_STR); + } + + $oContact = null; + $iIdContact = 0; + + $aContacts = $this->getContactsFromPDO( + $this->prepareAndExecute($sSql, $aParams) + ); + + return $aContacts ? $aContacts[0] : null; + } + /** * @param mixed $mID */ diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/dns.php b/snappymail/v/0.0.0/app/libraries/snappymail/dns.php index 302f2e841..cb3ebda20 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/dns.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/dns.php @@ -4,6 +4,10 @@ namespace SnappyMail; abstract class DNS { + /** + * $domain = 'bimigroup.org' + * Then a TXT lookup is done on 'default._bimi.bimigroup.org' + */ public static function BIMI(string $domain) : string { $oCache = \RainLoop\Api::Actions()->Cacher(); @@ -18,7 +22,7 @@ abstract class DNS } if (null === $BIMI) { $BIMI = ''; - $values = \dns_get_record($domain, \DNS_TXT); + $values = \dns_get_record("default._bimi.{$domain}", \DNS_TXT); if ($values) { foreach ($values as $value) { if (\str_starts_with($value['txt'], 'v=BIMI1')) { From 0deedbecf568ead79b680bb2b7ef2a3676adf0ea Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 10:22:49 +0100 Subject: [PATCH 086/114] Use caching and a fetch queue to fetch avatars slowly without overloading server #115 --- plugins/avatars/avatars.js | 96 ++++++++++++++++++++++++++------------ plugins/avatars/index.php | 51 +++++++++++++++----- plugins/avatars/style.css | 19 ++++++-- 3 files changed, 122 insertions(+), 44 deletions(-) diff --git a/plugins/avatars/avatars.js b/plugins/avatars/avatars.js index f18dbdf7f..7265be123 100644 --- a/plugins/avatars/avatars.js +++ b/plugins/avatars/avatars.js @@ -1,8 +1,57 @@ (rl => { // if (rl.settings.get('Nextcloud')) const - templateId = 'MailMessageView'; -// DATA_IMAGE_USER_DOT_PIC = 'data:image/svg+xml;utf8,'; + queue = [], + avatars = new Map, + templateId = 'MailMessageView', + getAvatarUid = msg => { + let from = msg.from[0], + bimi = 'pass' == from.dkimStatus ? 1 : 0; + return `${bimi}/${from.email}`; + }, + getAvatar = msg => avatars.get(getAvatarUid(msg)), + runQueue = (() => { + let item = queue.shift(); + while (item) { + let url = getAvatar(item[0]); + if (url) { + item[1](url); + item = queue.shift(); + } else { + // TODO: fetch vCard from Nextcloud contacts +// let cfg = rl.settings.get('Nextcloud'), + + let from = item[0].from[0]; + rl.pluginRemoteRequest((iError, data) => { + if (!iError && data?.Result.type) { + url = `data:${data.Result.type};base64,${data.Result.data}`; + avatars.set(getAvatarUid(item[0]), url); + item[1](url); + } + runQueue(); + }, 'Avatar', { + bimi: 'pass' == from.dkimStatus ? 1 : 0, + email: from.email + }); + break; + } + } + }).debounce(1000); + + ko.bindingHandlers.fromPic = { + init: (element, self, dummy, msg) => { + if (msg) { + let url = getAvatar(msg), + fn = url=>{element.src = url}; + if (url) { + fn(url); + } else { + queue.push([msg, fn]); + runQueue(); + } + } + } + }; addEventListener('rl-view-model.create', e => { if (templateId === e.detail.viewModelTemplateID) { @@ -24,39 +73,28 @@ view.message.subscribe(msg => { view.viewUserPicVisible(false); if (msg) { - let from = msg.from[0], - bimi = 'pass' == from.dkimStatus ? 1 : 0; -// view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`); -// view.viewUserPicVisible(true); - rl.pluginRemoteRequest((iError, data) => { - if (!iError && data?.Result.type) { - view.viewUserPic(`data:${data.Result.type};base64,${data.Result.data}`); + let url = getAvatar(msg), + fn = url => { + view.viewUserPic(url); view.viewUserPicVisible(true); - } - }, 'Avatar', { - bimi: bimi, - email: from.email - }); + }; + if (url) { + fn(url); + } else { +// let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0; +// view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`); +// view.viewUserPicVisible(true); + queue.push([msg, fn]); + runQueue(); + } } }); } -/* + if ('MailMessageList' === e.detail.viewModelTemplateID) { - const - template = document.getElementById('MailMessageList' ), - messageCheckbox = template.content.querySelector('.messageCheckbox'); - messageCheckbox.dataset.bind = 'attr:{style:$root.viewUserPic($data)}'; - e.detail.viewUserPic = msg => { - let from = msg.from[0], - bimi = 'pass' == from.dkimStatus ? 1 : 0; - return `background:no-repeat url("?Avatar/${bimi}/${encodeURIComponent(from.email)}") center / contain`; - return `background:no-repeat url("?Avatar/${bimi}/${encodeURIComponent(from.email)}") right / 32px;width:68px`; - }; - .checkboxMessage { - background: #000; - } + document.getElementById('MailMessageList').content.querySelector('.messageCheckbox') + .append(Element.fromHTML(``)); } -*/ }); })(window.rl); diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index 61a8547c4..f23acc1f1 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -58,20 +58,38 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $aResult = null; - // TODO: lookup contacts vCard - $oAccount = $oActions->getAccountFromToken(); - if ($oAccount) { - $oAddressBookProvider = $oActions->AddressBookProvider($oAccount); - if ($oAddressBookProvider) { - $oContact = $oAddressBookProvider->GetContactByEmail($sEmail); - if ($oContact && $oContact->vCard && $oContact->vCard['PHOTO']) { - $aResult = [ - 'text/vcard', - $oContact->vCard - ]; + $sAsciiEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); + $sEmailId = \sha1(\strtolower($sAsciiEmail)); + + $sFile = \APP_PRIVATE_DATA . 'avatars/' . $sEmailId; + $aFiles = \glob("{$sFile}.*"); + if ($aFiles) { + $aResult = [ + \mime_content_type($aFiles[0]), + \file_get_contents($aFiles[0]) + ]; + $oActions->cacheByKey($sEmail); + return $aResult; + } + + // TODO: lookup contacts vCard and return PHOTO value + /* + if (!$aResult) { + $oAccount = $oActions->getAccountFromToken(); + if ($oAccount) { + $oAddressBookProvider = $oActions->AddressBookProvider($oAccount); + if ($oAddressBookProvider) { + $oContact = $oAddressBookProvider->GetContactByEmail($sEmail); + if ($oContact && $oContact->vCard && $oContact->vCard['PHOTO']) { + $aResult = [ + 'text/vcard', + $oContact->vCard + ]; + } } } } + */ if (!$aResult) { $sDomain = \explode('@', $sEmail); @@ -89,7 +107,6 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin } // TODO: make Gravatar optional - $sAsciiEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); $aUrls[] = 'http://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404'; foreach ($aUrls as $sUrl) { @@ -99,6 +116,16 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin } } + if ($aResult) { + if (!\is_dir(\APP_PRIVATE_DATA . 'avatars')) { + \mkdir(\APP_PRIVATE_DATA . 'avatars', 0700); + } + \file_put_contents( + $sFile . \SnappyMail\File\MimeType::toExtension($aResult[0]), + $aResult[1] + ); + } + if (!$aResult) { $aServices = [ "services/{$sDomain}", diff --git a/plugins/avatars/style.css b/plugins/avatars/style.css index dc7eeb2e3..c9fadcd86 100644 --- a/plugins/avatars/style.css +++ b/plugins/avatars/style.css @@ -3,9 +3,22 @@ float: left; width: 50px; height: 50px; - padding: 2px; margin: 0 5px 0 0; - background: #fff; - border: 1px solid #ccc; + border: 1px solid var(--border-color, #ddd); border-radius: 6px; } +/* +.checkboxMessage { + background: #000 no-repeat center / contain; + background: #000 no-repeat right / 32px; + width:68px +} +*/ +#rl-app .messageCheckbox { + padding-right:0; + white-space:nowrap; +} +.messageCheckbox .fromPic { + margin: -0.6em 0 -0.6em 0.4em; + height: 2em; +} From 59cd22625adf095bb37ad6236b5ff64900f71d30 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 10:34:54 +0100 Subject: [PATCH 087/114] Make Gravatar and BIMI optional #115 --- plugins/avatars/index.php | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index f23acc1f1..24a834d8c 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -47,6 +47,18 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin return false; } + protected function configMapping() : array + { + return array( + \RainLoop\Plugins\Property::NewInstance('bimi')->SetLabel('Use BIMI (https://bimigroup.org/)') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDefaultValue(false), + \RainLoop\Plugins\Property::NewInstance('gravatar')->SetLabel('Use Gravatar') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDefaultValue(false) + ); + } + private static function getAvatar(string $sEmail, bool $bBimi) : ?array { if (!\strpos($sEmail, '@')) { @@ -97,17 +109,20 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $aUrls = []; - $BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null; - if ($BIMI) { - $aUrls[] = $BIMI; -// $aResult = ['text/uri-list', $BIMI]; - \SnappyMail\Log::debug('Avatar', "BIMI {$sDomain} for {$sUrl}"); - } else { - \SnappyMail\Log::notice('Avatar', "BIMI 404 for {$sDomain}"); + if ($this->Config()->Get('plugin', 'bimi', false)) { + $BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null; + if ($BIMI) { + $aUrls[] = $BIMI; +// $aResult = ['text/uri-list', $BIMI]; + \SnappyMail\Log::debug('Avatar', "BIMI {$sDomain} for {$sUrl}"); + } else { + \SnappyMail\Log::notice('Avatar', "BIMI 404 for {$sDomain}"); + } } - // TODO: make Gravatar optional - $aUrls[] = 'http://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404'; + if ($this->Config()->Get('plugin', 'gravatar', false)) { + $aUrls[] = 'http://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404'; + } foreach ($aUrls as $sUrl) { if ($aResult = static::getUrl($sUrl)) { From 77ab2fddf575f812a0494fe945bb9cc30f115946 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 10:46:12 +0100 Subject: [PATCH 088/114] Bugfix Avatars and simple empty-contact.png --- plugins/avatars/images/empty-contact.png | Bin 1881 -> 665 bytes plugins/avatars/index.php | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/avatars/images/empty-contact.png b/plugins/avatars/images/empty-contact.png index b2f2071d177ee53ecfaa3e46f780e9794cf05530..4418810b82e90b24e9ffb514ec673e25c76b964e 100644 GIT binary patch literal 665 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 T6R?S>00000NkvXXu0mjf(mH~* diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index 24a834d8c..aa1204af5 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -25,7 +25,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin { $bBimi = !empty($this->jsonParam('bimi')); $sEmail = $this->jsonParam('email'); - $aResult = static::getAvatar(\urldecode($sEmail), !empty($sEmail)); + $aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail)); if ($aResult) { $aResult = [ 'type' => $aResult[0], @@ -38,7 +38,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin // public function ServiceAvatar(...$aParts) public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail) { - $aResult = static::getAvatar(\urldecode($sEmail), !empty($sEmail)); + $aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail)); if ($aResult) { \header('Content-Type: '.$aResult[0]); echo $aResult[1]; @@ -59,7 +59,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin ); } - private static function getAvatar(string $sEmail, bool $bBimi) : ?array + private function getAvatar(string $sEmail, bool $bBimi) : ?array { if (!\strpos($sEmail, '@')) { return null; From 288e1c874224090dffa7a3d9a5a5e038924dd392 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 11:03:47 +0100 Subject: [PATCH 089/114] Resolve #693 --- snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php index 4a84b4835..9dcbd446d 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php @@ -9,7 +9,10 @@ class Login extends \SnappyMail\SASL public function authenticate(string $username, string $passphrase, ?string $challenge = null) : string { - if ($challenge && 'Username:' !== $this->decode($challenge)) { + // $challenge should be 'VXNlcm5hbWU6', but broken on some systems + // See https://github.com/the-djmaze/snappymail/issues/693 + if ($challenge && \str_starts_with($this->decode($challenge), 'Username:')) { +// if ($challenge && 'Username:' !== $this->decode($challenge)) { throw new \Exception("Invalid response: {$challenge}"); } $this->passphrase = $passphrase; @@ -18,6 +21,7 @@ class Login extends \SnappyMail\SASL public function challenge(string $challenge) : ?string { + // $challenge should be 'UGFzc3dvcmQ6' if ($challenge && 'Password:' !== $this->decode($challenge)) { throw new \Exception("invalid response: {$challenge}"); } From 6d4f626cd69621ffad4d9af77d7b4863038564c1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 11:08:48 +0100 Subject: [PATCH 090/114] Prevent domain uppercase issues https://github.com/the-djmaze/snappymail/issues/689#issuecomment-1324091032 --- snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php index b2cad3769..0f7b8abe8 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Domain.php @@ -57,7 +57,7 @@ class Domain extends AbstractProvider public function LoadOrCreateNewFromAction(\RainLoop\Actions $oActions, string $sNameForTest = null) : ?\RainLoop\Model\Domain { - $sName = (string) $oActions->GetActionParam('Name', ''); + $sName = \mb_strtolower((string) $oActions->GetActionParam('Name', '')); if (\strlen($sName) && $sNameForTest && !\str_contains($sName, '*')) { $sNameForTest = null; } From b7fd9334994ec27f066c364aa911df382b204131 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 11:22:20 +0100 Subject: [PATCH 091/114] Resolve #691 --- dev/View/User/MailBox/MessageView.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 4855fccac..a589de2bc 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -265,8 +265,9 @@ export class MailMessageView extends AbstractViewRight { return; } - if (eqs(event, '.attachmentsPlace .attachmentIconParent')) { + if (eqs(event, '.attachmentsPlace .showPreview')) { event.stopPropagation(); + return; } el = eqs(event, '.attachmentsPlace .showPreplay'); @@ -287,9 +288,10 @@ export class MailMessageView extends AbstractViewRight { // no default } } + return; } - el = eqs(event, '.attachmentsPlace .attachmentName'); + el = eqs(event, '.attachmentItem'); if (el) { const attachment = ko.dataFor(el), url = attachment?.linkDownload(); if (url) { From 2053749b54f57212316d6f72c1f2803fdbfb5a5a Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 11:35:34 +0100 Subject: [PATCH 092/114] Added description to Avatars extension --- plugins/avatars/avatars.js | 2 +- plugins/avatars/index.php | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/avatars/avatars.js b/plugins/avatars/avatars.js index 7265be123..ba872301e 100644 --- a/plugins/avatars/avatars.js +++ b/plugins/avatars/avatars.js @@ -7,7 +7,7 @@ getAvatarUid = msg => { let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0; - return `${bimi}/${from.email}`; + return `${bimi}/${from.email.toLowerCase()}`; }, getAvatar = msg => avatars.get(getAvatarUid(msg)), runQueue = (() => { diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index aa1204af5..e41d11596 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -7,11 +7,11 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin AUTHOR = 'SnappyMail', URL = 'https://snappymail.eu/', VERSION = '1.0', - RELEASE = '2022-11-11', + RELEASE = '2022-11-23', REQUIRED = '2.22.0', CATEGORY = 'Contacts', LICENSE = 'MIT', - DESCRIPTION = ''; + DESCRIPTION = 'Show photo of sender in message and messages list (supports BIMI and Gravatar, Contacts is still TODO)'; public function Init() : void { @@ -21,6 +21,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $this->addPartHook('Avatar', 'ServiceAvatar'); } + /** + * POST method handling + */ public function DoAvatar() : array { $bBimi = !empty($this->jsonParam('bimi')); @@ -35,6 +38,11 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin return $this->jsonResponse(__FUNCTION__, $aResult); } + /** + * GET /?Avatar/${bimi}/${from.email} + * Not fond of this idea because email address is exposed + * Maybe use btoa(from.email) or Crypto.subtle.encrypt({name:'AES-GCM',iv:''}, token, from.email) + */ // public function ServiceAvatar(...$aParts) public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail) { From 5df476a7d0e3bd71964f0a1defbdd2d9eb2b12aa Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 12:32:57 +0100 Subject: [PATCH 093/114] Improved avatar style a bit --- plugins/avatars/index.php | 1 + plugins/avatars/style.css | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index e41d11596..fb3145bf9 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -153,6 +153,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $aServices = [ "services/{$sDomain}", 'services/' . \preg_replace('/^.+\\.([^.]+\\.[^.]+)$/D', '$1', $sDomain), + 'services/' . \preg_replace('/^(.+\\.)?(paypal\\.[a-z][a-z])$/D', 'paypal.com', $sDomain), 'empty-contact' // DATA_IMAGE_USER_DOT_PIC ]; foreach ($aServices as $service) { diff --git a/plugins/avatars/style.css b/plugins/avatars/style.css index c9fadcd86..e2e21bac4 100644 --- a/plugins/avatars/style.css +++ b/plugins/avatars/style.css @@ -1,11 +1,11 @@ -.messageView .b-content .messageItemHeader .fromPic { - display: inline-block; +.messageView .fromPic { + border: 1px solid var(--border-color, #ddd); float: left; width: 50px; height: 50px; - margin: 0 5px 0 0; - border: 1px solid var(--border-color, #ddd); - border-radius: 6px; + margin: 0 4px 0 0; + padding: 1px; + border-radius: var(--border-radius, 5px); } /* .checkboxMessage { @@ -19,6 +19,7 @@ white-space:nowrap; } .messageCheckbox .fromPic { - margin: -0.6em 0 -0.6em 0.4em; - height: 2em; + margin: 0 0 -0.4em 0.4em; + height: 1.5em; + max-width: 1.5em; } From da98c611e12680d1898add7b7aa32a352b7953fc Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 12:57:44 +0100 Subject: [PATCH 094/114] v2.22.0 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 22 +++++++++++++++++++ integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- 7 files changed, 28 insertions(+), 6 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index fc32c0018..0d04adea6 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.21.4" +current = "2.22.0" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index bfcbdc35c..c93ee467e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 2.22.0 – 2022-11-23 + +### Added +- AddressBookInterface::GetContactByEmail() to support sender image/avatar extension + [#115](https://github.com/the-djmaze/snappymail/pull/115) + +### Changed +- All the attachment zone is not clickable, even if the cursor is a hand + [#691](https://github.com/the-djmaze/snappymail/pull/691) +- Different approach for "update button duplicated in admin panel" + [#677](https://github.com/the-djmaze/snappymail/pull/677) +- Better drag & drop solution for leftPanel + +### Fixed +- The page does not change after batch deletion + [#684](https://github.com/the-djmaze/snappymail/pull/684) +- Prevent domain uppercase issues found in + [#689](https://github.com/the-djmaze/snappymail/pull/689) +- Login invalid response: VXNlcm5hbWU6CG + [#693](https://github.com/the-djmaze/snappymail/pull/693) + + ## 2.21.4 – 2022-11-22 ### Added diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 437a8e168..8d16ef18e 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.21.4. +This app packages SnappyMail 2.22.0. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index bddbb47a3..66102f0c1 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.21.4 +VERSION=2.22.0 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index 15bb98944..326f799ef 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.21.4 + 2.22.0 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli Date: Wed, 23 Nov 2022 16:26:26 +0100 Subject: [PATCH 095/114] Resolve #695 --- .../app/libraries/snappymail/sasl/login.php | 5 ++-- test/sasl.php | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/sasl.php diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php index 9dcbd446d..52608a49f 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/sasl/login.php @@ -11,9 +11,8 @@ class Login extends \SnappyMail\SASL { // $challenge should be 'VXNlcm5hbWU6', but broken on some systems // See https://github.com/the-djmaze/snappymail/issues/693 - if ($challenge && \str_starts_with($this->decode($challenge), 'Username:')) { -// if ($challenge && 'Username:' !== $this->decode($challenge)) { - throw new \Exception("Invalid response: {$challenge}"); + if ($challenge && !\str_starts_with($this->decode($challenge), 'Username:')) { + throw new \Exception("Invalid response: {$this->decode($challenge)}"); } $this->passphrase = $passphrase; return $this->encode($username); diff --git a/test/sasl.php b/test/sasl.php new file mode 100644 index 000000000..f36d238e4 --- /dev/null +++ b/test/sasl.php @@ -0,0 +1,25 @@ +base64 = true; +var_dump($LOGIN->authenticate('john', 'doe', 'VXNlcm5hbWU6')); +var_dump($LOGIN->authenticate('john', 'doe', 'VXNlcm5hbWU6CG')); +var_dump($LOGIN->authenticate('john', 'doe', 'UGFzc3dvcmQ6')); From 0d7ffcb0a59b7ed46132a707def2016dde5b08de Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Nov 2022 16:30:08 +0100 Subject: [PATCH 096/114] v2.22.1 --- .docker/release/files/usr/local/include/application.ini | 2 +- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- integrations/nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- plugins/kolab/index.php | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 0d04adea6..29fefb610 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.22.0" +current = "2.22.1" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 8d16ef18e..4b44b7fa3 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.22.0. +This app packages SnappyMail 2.22.1. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 66102f0c1..88134b8e5 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.22.0 +VERSION=2.22.1 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index 326f799ef..ee192e6f0 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.22.0 + 2.22.1 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli Date: Thu, 24 Nov 2022 09:55:03 +0100 Subject: [PATCH 097/114] Update langs.json Add Basque language --- snappymail/v/0.0.0/app/localization/langs.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snappymail/v/0.0.0/app/localization/langs.json b/snappymail/v/0.0.0/app/localization/langs.json index bf0ac8dc7..7d530d501 100644 --- a/snappymail/v/0.0.0/app/localization/langs.json +++ b/snappymail/v/0.0.0/app/localization/langs.json @@ -14,6 +14,7 @@ "es-ES": "Español (España)", "es-LA": "Español (América Latina)", "et-EE": "Eesti", + "eu": "Euskara", "fa-IR": "‏فارسی‏", "fi-FI": "Suomi", "fr-CA": "Français (Canada)", @@ -60,6 +61,7 @@ "es-ES": "Spanish (Spain)", "es-LA": "Spanish (Latin America)", "et-EE": "Estonian", + "eu": "Basque", "fa-IR": "Persian", "fi-FI": "Finnish", "fr-CA": "French (Canada)", From e067079f8aac9792cc48e21334fd9ca672c0448a Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 11:31:12 +0100 Subject: [PATCH 098/114] Fixes cPanel CSS issue #697 --- .../var/cpanel/webmail/webmail_snappymail.yaml | 3 +++ plugins/login-remote/index.php | 8 ++++++++ snappymail/v/0.0.0/themes/SnowDarkV1/styles.css | 2 +- snappymail/v/0.0.0/themes/SquaresDark/styles.css | 12 ++++++------ vendors/bootstrap/less/forms.less | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml diff --git a/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml b/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml new file mode 100644 index 000000000..30a093c44 --- /dev/null +++ b/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml @@ -0,0 +1,3 @@ +url: "/3rdparty/snappymail/" +displayname: "SnappyMail" +icon: "/3rdparty/snappymail/snappymail/v/2.22.1/static/logo-text.png" diff --git a/plugins/login-remote/index.php b/plugins/login-remote/index.php index 9efba76b9..3abbfd004 100644 --- a/plugins/login-remote/index.php +++ b/plugins/login-remote/index.php @@ -16,6 +16,14 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin public function Init() : void { $this->addPartHook('RemoteAutoLogin', 'ServiceRemoteAutoLogin'); + $this->addHook('filter.app-data', 'FilterAppData'); + } + + public function FilterAppData($bAdmin, &$aResult) + { + if (!$bAdmin && \is_array($aResult) && empty($aResult['Auth']) && isset($_ENV['REMOTE_USER'])) { + $aResult['DevEmail'] = $this->Config()->Get('plugin', 'email', $_ENV['REMOTE_USER']); + } } public function ServiceRemoteAutoLogin() : bool diff --git a/snappymail/v/0.0.0/themes/SnowDarkV1/styles.css b/snappymail/v/0.0.0/themes/SnowDarkV1/styles.css index 2ea19b377..4ff960a1d 100644 --- a/snappymail/v/0.0.0/themes/SnowDarkV1/styles.css +++ b/snappymail/v/0.0.0/themes/SnowDarkV1/styles.css @@ -172,7 +172,7 @@ input, textarea, .select, select, .emailaddresses { color: #fff; } .select, select { - background-image: url("data:image/svg+xml,"); + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGhlaWdodD0nMTBweCcgd2lkdGg9JzE1cHgnPjx0ZXh0IHg9JzAnIHk9JzEwJyBmaWxsPSdyZ2IoMjU1LDI1NSwyNTUpJz7ilr48L3RleHQ+PC9zdmc+); } hr { diff --git a/snappymail/v/0.0.0/themes/SquaresDark/styles.css b/snappymail/v/0.0.0/themes/SquaresDark/styles.css index 39661fd04..8e418647e 100644 --- a/snappymail/v/0.0.0/themes/SquaresDark/styles.css +++ b/snappymail/v/0.0.0/themes/SquaresDark/styles.css @@ -45,6 +45,11 @@ /* MESSAGE LIST */ --message-list-toolbar-bg-color: #222; + + /* FORMS */ + --input-clr: #fff; + --input-bg-clr: #000; + --input-border-clr: #aaa; } ::-webkit-scrollbar { @@ -144,13 +149,8 @@ background: linear-gradient(#555 0%, #000 100%); } -input, textarea, .select, select, .emailaddresses { - background-color: #000; - border-color: #aaa; - color: #fff; -} .select, select { - background-image: url("data:image/svg+xml,"); + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGhlaWdodD0nMTBweCcgd2lkdGg9JzE1cHgnPjx0ZXh0IHg9JzAnIHk9JzEwJyBmaWxsPSdyZ2IoMjU1LDI1NSwyNTUpJz7ilr48L3RleHQ+PC9zdmc+); } hr { diff --git a/vendors/bootstrap/less/forms.less b/vendors/bootstrap/less/forms.less index 26657a059..267f48c45 100644 --- a/vendors/bootstrap/less/forms.less +++ b/vendors/bootstrap/less/forms.less @@ -83,7 +83,7 @@ select, .select { // Safari requires -webkit- -webkit-appearance: none; appearance: none; - background: var(--input-bg-clr, #fff) url("data:image/svg+xml,") right center/1em no-repeat border-box; + background: var(--input-bg-clr, #fff) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGhlaWdodD0nMTBweCcgd2lkdGg9JzE1cHgnPjx0ZXh0IHg9JzAnIHk9JzEwJyBmaWxsPSdyZ2IoODUsODUsODUpJz7ilr48L3RleHQ+PC9zdmc+) right center/1em no-repeat border-box; border: 1px solid var(--input-border-clr, #ccc); cursor: pointer; padding-right: 1.5em; From cd66ce1d289c2dcf9589fa75c807a17fd238fe0e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 11:35:56 +0100 Subject: [PATCH 099/114] Resolve #696 --- snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index d9037efe1..1c4432568 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -739,7 +739,7 @@ class Actions 'Url' => '', 'User' => '' ]; - $aData['Password'] = $aData['Password'] ? static::APP_DUMMY : ''; + $aData['Password'] = empty($aData['Password']) ? '' : static::APP_DUMMY; $aData['Interval'] = \max(20, \min(320, (int) $oConfig->Get('contacts', 'sync_interval', 20))); $aResult['ContactsSync'] = $aData; } From 29473045f8bed04e92b8805c30e72805b479e4d1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 12:20:57 +0100 Subject: [PATCH 100/114] Fix cPanel automate login issue --- .../cpanel/webmail/webmail_snappymail.yaml | 2 +- plugins/login-remote/index.php | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml b/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml index 30a093c44..682f79565 100644 --- a/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml +++ b/integrations/cpanel/var/cpanel/webmail/webmail_snappymail.yaml @@ -1,3 +1,3 @@ -url: "/3rdparty/snappymail/" +url: "/3rdparty/snappymail/?RemoteAutoLogin" displayname: "SnappyMail" icon: "/3rdparty/snappymail/snappymail/v/2.22.1/static/logo-text.png" diff --git a/plugins/login-remote/index.php b/plugins/login-remote/index.php index 3abbfd004..cc2dfce52 100644 --- a/plugins/login-remote/index.php +++ b/plugins/login-remote/index.php @@ -6,8 +6,8 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin NAME = 'Login Remote', AUTHOR = 'SnappyMail', URL = 'https://snappymail.eu/', - VERSION = '1.0', - RELEASE = '2022-11-11', + VERSION = '1.1', + RELEASE = '2022-11-24', REQUIRED = '2.21.0', CATEGORY = 'Login', LICENSE = 'MIT', @@ -17,15 +17,18 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin { $this->addPartHook('RemoteAutoLogin', 'ServiceRemoteAutoLogin'); $this->addHook('filter.app-data', 'FilterAppData'); + $this->addHook('login.credentials', 'FilterLoginCredentials'); } public function FilterAppData($bAdmin, &$aResult) { if (!$bAdmin && \is_array($aResult) && empty($aResult['Auth']) && isset($_ENV['REMOTE_USER'])) { - $aResult['DevEmail'] = $this->Config()->Get('plugin', 'email', $_ENV['REMOTE_USER']); + $aResult['DevEmail'] = $_ENV['REMOTE_USER']; +// $aResult['DevPassword'] = $_ENV['REMOTE_PASSWORD']; } } + private static bool $login = false; public function ServiceRemoteAutoLogin() : bool { $oActions = \RainLoop\Api::Actions(); @@ -36,10 +39,10 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin $sEmail = $_ENV['REMOTE_USER'] ?? ''; $sPassword = $_ENV['REMOTE_PASSWORD'] ?? ''; - if (\strlen($sEmail) && \strlen($sPassword)) - { + if (\strlen($sEmail) && \strlen($sPassword)) { try { + static::$login = true; $oAccount = $oActions->LoginProcess($sEmail, $sPassword); if ($oAccount instanceof \RainLoop\Model\MainAccount) { $oActions->SetAuthToken($oAccount); @@ -56,4 +59,16 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin return true; } + public function FilterLoginCredentials(&$sEmail, &$sLogin, &$sPassword) + { + // cPanel https://github.com/the-djmaze/snappymail/issues/697 + if (static::$login/* && $sLogin == $_SERVER['REMOTE_USER']*/) { + $iPos = \strpos($sPassword, '[::cpses::]'); + if ($iPos) { + $sKey = \substr($sPassword, 0, $iPos); + $sLogin = $_SERVER['REMOTE_USER'] . '/' . \substr($sPassword, 0, $iPos); + } + } + } + } From 1e430c8e5793a976177eaf454a67020eaec60904 Mon Sep 17 00:00:00 2001 From: Olloki Date: Thu, 24 Nov 2022 12:35:12 +0100 Subject: [PATCH 101/114] Basque user.json Basque translation on user.json --- .../v/0.0.0/app/localization/eu/user.json | 556 ++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 snappymail/v/0.0.0/app/localization/eu/user.json diff --git a/snappymail/v/0.0.0/app/localization/eu/user.json b/snappymail/v/0.0.0/app/localization/eu/user.json new file mode 100644 index 000000000..0b76a0da7 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/eu/user.json @@ -0,0 +1,556 @@ +{ + "GLOBAL": { + "TO_ARCHIVE": "Artxibatu", + "ARE_YOU_SURE": "Ziur zaude", + "BACK": "Atzera", + "BCC": "BCC", + "CANCEL": "Utzi", + "CC": "CC", + "CLEAR": "Garbitu", + "CLOSE": "Itxi", + "CONTACTS": "Kontaktuak", + "DELETE": "Ezabatu", + "DONE": "Eginda", + "EMAIL": "Email", + "FROM": "Nork", + "LOADING": "Kargatzen", + "LOGOUT": "Saioa itxi", + "MORE": "Gehiago", + "MOVE_TO": "Mugitu", + "NAME": "Izena", + "NONE": "Bat ere ez", + "NOT_SPAM": "Ez da spama", + "USERNAME": "Erabiltzaile-izena", + "PASSWORD": "Pasahitza", + "REPLY_TO": "Erantzun", + "SAVE": "Gorde", + "SAVE_CHANGES": "Aldaketak gorde?", + "SEARCH": "Bilatu", + "SPAM": "Spam", + "SUBJECT": "Gaia", + "TEST": "Probatu", + "TO": "Nori", + "SERVER_MESSAGE": "Zerbitzariko mezua", + "YES": "Bai", + "NO": "Ez", + "INACTIVE": "Inaktibo" + }, + "LOGIN": { + "LABEL_LOGIN": "Login", + "LABEL_SIGN_ME": "Gogora nazazu", + "BUTTON_SIGN_IN": "Sartu" + }, + "TOP_TOOLBAR": { + "BUTTON_ADD_ACCOUNT": "Gehitu kontua", + "BUTTON_SETTINGS": "Ezarpenak", + "BUTTON_HELP": "Laguntza" + }, + "MOBILE": { + "BUTTON_MOBILE_VERSION": "Mugikorreko bertsioa", + "BUTTON_DESKTOP_VERSION": "Mahaigaineko bertsioa" + }, + "SEARCH": { + "TITLE_ADV": "Bilaketa aurreratua", + "TEXT": "Testua", + "HAS_ATTACHMENT": "Erantsitako fitxategiak", + "FLAGGED": "Markatuta", + "UNSEEN": "Irakurri gabe", + "DATE": "Data", + "DATE_ALL": "Denak", + "DATE_3_DAYS": "3 egun arterakoak", + "DATE_7_DAYS": "Astebetako arterakoak", + "DATE_MONTH": "Hilabete arterakoak", + "DATE_3_MONTHS": "3 hilabete arterakoak", + "DATE_6_MONTHS": "6 hilabete arterakoak", + "DATE_YEAR": "Urtebete arterakoak", + "SUBFOLDERS": "Azpikarpetak", + "SUBFOLDERS_NONE": "Bat ere ez", + "SUBFOLDERS_SUBTREE": "Denak", + "SUBFOLDERS_SUBTREE_ONE": "Maila bat", + "REPLIED": "Erantzunda", + "KEYWORD": "Etiketa" + }, + "FOLDER_LIST": { + "BUTTON_COMPOSE": "Idatzi", + "BUTTON_NEW_MESSAGE": "Mezu berria", + "INBOX_NAME": "Sarrera-ontzia", + "SENT_NAME": "Bidalitakoak", + "DRAFTS_NAME": "Zirriborroak", + "TRASH_NAME": "Zakarrontzia", + "ARCHIVE_NAME": "Artxiboa" + }, + "QUOTA": { + "TITLE": "Kuota" + }, + "MESSAGE_LIST": { + "BUTTON_RELOAD": "Eguneratu mezuen zerrenda", + "BUTTON_EMPTY_FOLDER": "Hustu karpeta", + "BUTTON_MULTY_FORWARD": "Erantsi bezala bidali", + "BUTTON_DELETE_WITHOUT_MOVE": "Beteriako ezabatu", + "MENU_SET_SEEN": "Irakurri bezala markatu", + "MENU_SET_ALL_SEEN": "Dena irakurri bezala markatu", + "MENU_UNSET_SEEN": "Irakurri gabe bezala markatu", + "MENU_SET_FLAG": "Markatu", + "MENU_UNSET_FLAG": "Desmarkatu", + "MENU_SELECT_ALL": "Denak", + "MENU_SELECT_INVERT": "Alderantzikatu", + "MENU_SELECT_UNSEEN": "Irakurri gabea", + "MENU_SELECT_SEEN": "Irakurria", + "MENU_SELECT_FLAGGED": "Markatua", + "MENU_SELECT_UNFLAGGED": "Markatu gabea", + "DOWNLOAD_ALL_ATTACHMENTS": "Deskargatu eranskin guztiak", + "EMPTY_LIST": "Hustu zerrenda.", + "EMPTY_SEARCH_LIST": "Zure bilaketarekin bat datorren mezurik ez.", + "SEARCH_RESULT_FOR": "\"%SEARCH%\"-erako bilaketa emaitzak", + "BACK_TO_MESSAGE_LIST": "itzuli mezuen zerrendara", + "PUT_MESSAGE_HERE": "Bota mezua hemen zerrendan ikusteko", + "TODAY_AT": "gaur %TIME%-etan", + "YESTERDAY_AT": "atzo %TIME%-etan", + "NEW_MESSAGE_NOTIFICATION": "%COUNT% mezu berri dituzu!", + "QUOTA_SIZE": "%SIZE% (%PROC%%)<\/strong> erabiliz zure %LIMIT%<\/strong>-tik", + "SORT": "Ordenatu", + "SORT_DATE_ASC": "Data goraka", + "SORT_DATE_DESC": "Data beheraka", + "SORT_SIZE_ASC": "Tamaina goraka", + "SORT_SIZE_DESC": "Tamaina beheraka", + "SORT_SUBJECT_ASC": "Gaia A-Z", + "SORT_SUBJECT_DESC": "Gaia Z-A", + "SORT_FROM_ASC": "Nork A-Z", + "SORT_FROM_DESC": "Nork Z-A" + }, + "MESSAGE": { + "BUTTON_EDIT": "Editatu", + "BUTTON_UNSUBSCRIBE": "Kendu zerrenda honen harpidetza", + "BUTTON_REPLY": "Erantzun", + "BUTTON_REPLY_ALL": "Erantzun denei", + "BUTTON_FORWARD": "Birbidali", + "BUTTON_FORWARD_AS_ATTACHMENT": "Birbidali eranskin moduan", + "BUTTON_EDIT_AS_NEW": "Editatu Berri moduan", + "BUTTON_SHOW_IMAGES": "Erakutsi kanpoko irudiak", + "BUTTON_NOTIFY_READ_RECEIPT": "Bidaltzaileak irakurri duzunean abisua jasotzeko eskatu du.", + "BUTTON_IN_NEW_WINDOW": "Ikusi kanpoko leihoan", + "BUTTON_THREAD_LIST": "Harien zerrenda", + "BUTTON_THREAD_PREV": "Aurrekoa", + "BUTTON_THREAD_NEXT": "Hurrengoa", + "BUTTON_THREAD_MORE": "Mezu gehiago", + "MENU_HEADERS": "Erakutsi mezuen goiburuak", + "MENU_VIEW_ORIGINAL": "Erakutsi jatorrizkoa", + "HTML_VIEW": "Ikusi HTML mezua", + "PLAIN_VIEW": "Ikusi testu soil mezua", + "MENU_DOWNLOAD_ORIGINAL": "Deskargatu .eml fitxategi bezala", + "MENU_FILTER_SIMILAR": "Iragazi hau bezalako mezuak", + "MENU_PRINT": "Inprimatu", + "EMPTY_SUBJECT_TEXT": "(Gairik ez)", + "LABEL_DATE": "Data", + "LABEL_FROM_SHORT": "nork", + "LABEL_TO_SHORT": "nori", + "PRINT_LABEL_ATTACHMENTS": "Eranskinak", + "MESSAGE_VIEW_DESC": "Aukeratu zerrendako edozein mezua hemen ikusteko.", + "MESSAGE_VIEW_MOVE_DESC": "Klikatu karpetaren izena ezkerreko panelean helburua aukeratzeko.", + "LINK_DOWNLOAD_AS_ZIP": "Deskargatu zip gisa", + "SPAM_SCORE": "Spam Puntuzioa", + "HAS_VIRUS_WARNING": "KONTUZ: virusa detektatu da", + "TAGS": "Etiketak", + "NEW_TAG": "Etiketa berria" + }, + "MESSAGE_TAGS": { + "$important": "Garrantzitsua", + "$todo": "Egiteko", + "$label1": "Garrantzitsua", + "$label2": "Lana", + "$label3": "Pertsonala", + "$label4": "Egiteko", + "$label5": "Beranduago" + }, + "READ_RECEIPT": { + "SUBJECT": "Itzultzeko agiria (bistaratua) - %SUBJECT%", + "BODY": "%READ-RECEIPT% -ra bidali duzun mezurako Itzultzeko agiria da hau.\n\nNote: \"Itzultzeko agiri honek mezua hartzailearen ordenagailuan agertu dela bakarrik jakinarazten du .\"\nHorrek ez du esan nahi hartzaileak edukia irakurri edota ulertu duenik.\n" + }, + "SUGGESTIONS": { + "SEARCHING_DESC": "Bilatzen..." + }, + "CONTACTS": { + "TAB_CONTACT": "Kontaktua", + "TAB_LOCATIONS": "Kokapenak", + "TAB_BUSINESS": "Lantegia", + "LABEL_ORGANIZATION": "Erakundea", + "LABEL_DEPARTMENT": "Saila", + "LABEL_TITLE": "Izenburua", + "TAB_CRYPTO": "Crypto", + "ASK": "Galdetu", + "NEVER": "Inoiz ez", + "ALWAYS": "Beti", + "ALWAYS_IF_POSSIBLE": "Beti aukera badago", + "BUTTON_ADD_CONTACT": "Gehitu Kontaktua", + "BUTTON_CREATE_CONTACT": "Sortu", + "BUTTON_UPDATE_CONTACT": "Eguneratu", + "BUTTON_IMPORT": "Inportatu (csv, vcf, vCard)", + "BUTTON_EXPORT_VCARD": "Esportatu (vcf, vCard)", + "BUTTON_EXPORT_CSV": "Esportatu (csv)", + "ERROR_IMPORT_FILE": "Inportazio errorea (fitxategi-formatua ez da baliozkoa)", + "EMPTY_LIST": "Ez dago kontakturik hemen", + "EMPTY_SEARCH": "Ez da kontakturik aurkitu", + "CLEAR_SEARCH": "Garbitu bilaketa", + "CONTACT_VIEW_DESC": "Aukeratu kontaktua zerrendan hemen ikusteko.", + "LABEL_BIRTHDAY": "Urtebetetzea", + "PLACEHOLDER_ENTER_DISPLAY_NAME": "Sartu erakusteko izena", + "PLACEHOLDER_ENTER_LAST_NAME": "Sartu abizenak", + "PLACEHOLDER_ENTER_FIRST_NAME": "Sartu izena", + "PLACEHOLDER_ENTER_NICK_NAME": "Sartu ezizena", + "LABEL_READ_ONLY": "Irakurtzeko bakarrik", + "ADD_MENU_LABEL": "Gehitu", + "ADD_MENU_NICKNAME": "Ezizena", + "ADD_MENU_NOTE": "Ezizena", + "ADD_MENU_PHONE": "Telefonoa", + "ADD_MENU_URL": "URL", + "ADD_MENU_ADDRESS": "Helbidea", + "ADD_MENU_BIRTHDAY": "Urtebetetzea", + "ADD_MENU_TAGS": "Etiketak", + "BUTTON_SHARE_ALL": "Denak", + "BUTTON_SYNC": "Sinkronizazioa (CardDAV)" + }, + "COMPOSE": { + "LINK_SHOW_INPUTS": "erakutsi eremu guztiak", + "BUTTON_SEND": "Bidali", + "BUTTON_MINIMIZE": "Minimizatu", + "SAVED_TIME": "%TIME%-n gordea", + "SAVED_ERROR_ON_SEND": "Mezua bidali da baina ez da gorde bidalitako elementuen karpetan", + "DISCARD_UNSAVED_DATA": "Baztertu gorde gabeko datuak?", + "ATTACH_FILES": "Erantsi fitxategiak", + "ATTACH_DROP_FILES_DESC": "Arrastatu fitxategiak hona", + "REPLY_MESSAGE_TITLE": "%DATETIME%, %EMAIL% idatzia", + "FORWARD_MESSAGE_TOP_TITLE": "-------- Birbidalitako mezua -------", + "FORWARD_MESSAGE_TOP_SENT": "Bidalita", + "EMPTY_TO_ERROR_DESC": "Mesedez, gehitu gutxienez jasotzaile bat", + "NO_ATTACHMENTS_HERE_DESC": "Ez dago eranskinik hemen.", + "ATTACHMENTS_ERROR_DESC": "Kontuz! Eranskin guztiak ez dira kargatu.", + "ATTACHMENTS_UPLOAD_ERROR_DESC": "Eranskin guztiak ez dira kargatu oraindik.", + "ALLOW_SPELLCHECK": "Gaitu nabigatzaileko hizkuntza-zuzentzailea", + "BUTTON_REQUEST_READ_RECEIPT": "Eskatu irakurketa-agiria", + "BUTTON_MARK_AS_IMPORTANT": "MArkatu garrantzitsu bezala", + "BUTTON_REQUEST_DSN": "Eskatu jasotze-agiria" + }, + "POPUPS_ASK": { + "DESC_WANT_CLOSE_THIS_WINDOW": "Ziur zaude leiho hau itxi nahi duzula?", + "DESC_WANT_DELETE_MESSAGES": "Ziur zaude mezua(k) ezabatu nahi d(it)uzula?" + }, + "POPUPS_LANGUAGES": { + "TITLE_LANGUAGES": "Aukeratu zure hizkuntza" + }, + "POPUPS_ADD_ACCOUNT": { + "TITLE_ADD_ACCOUNT": "Kontua gehitu?", + "BUTTON_ADD_ACCOUNT": "Gehitu", + "TITLE_UPDATE_ACCOUNT": "Kontua eguneratu?", + "BUTTON_UPDATE_ACCOUNT": "Eguneratu" + }, + "POPUPS_IDENTITY": { + "TITLE_ADD_IDENTITY": "Nortasuna gehitu?", + "TITLE_UPDATE_IDENTITY": "Nortasuna eguneratu?", + "BUTTON_ADD_IDENTITY": "Gehitu", + "BUTTON_UPDATE_IDENTITY": "Eguneratu", + "LABEL_SIGNATURE_INSERT_BEFORE": "Txertatu sinadura hau erantzuteko testuaren aurretik" + }, + "POPUPS_CREATE_FOLDER": { + "TITLE_CREATE_FOLDER": "Karpeta sortu?", + "LABEL_PARENT": "Karpeta gurasoa", + "BUTTON_CREATE": "Sortu", + "TITLE_CREATING_PROCESS": "Karpeta sortzen" + }, + "POPUPS_CLEAR_FOLDER": { + "TITLE_CLEAR_FOLDER": "Ezabatu karpetako mezu guztiak?", + "DANGER_DESC_WARNING": "Kontuz!", + "DANGER_DESC_HTML_1": "Ekintza honen bidez %FOLDER% karpetako mezu guztiak ezabatuko dira.", + "DANGER_DESC_HTML_2": "Behin hasita, ekintza hau ezin da gelditu edo utzi.", + "TITLE_CLEARING_PROCESS": "Karpeta garbitzen..." + }, + "OPENPGP": { + "POPUP_IMPORT_TITLE": "Inportatu OpenPGP gakoa", + "POPUP_IMPORT_BUTTON": "Inportatu", + "POPUP_VIEW_TITLE": "Ikusi OpenPGP gakoa", + "POPUP_VIEW_BUTTON": "Aukeratu", + "POPUP_GENERATE_TITLE": "Sortu OpenPGP gako parea", + "POPUP_GENERATE_BUTTON": "Sortu", + "LABEL_KEY_TYPE": "Mota", + "LABEL_SIGN": "Sinatu", + "LABEL_ENCRYPT": "Zifratu", + "BUTTON_DECRYPT": "Deszifratu", + "BUTTON_VERIFY": "Egiaztatu", + "SIGNED_MESSAGE": "OpenPGP sinatutako mezua", + "ENCRYPTED_MESSAGE": "OpenPGP zifratutako mezua", + "STORE_IN_GNUPG": "Zerbitzarian gorden GnuPG-n", + "BACKUP_ON_SERVER": "Zerbitzarian gorde (zifratuta)", + "GOOD_SIGNATURE": "%USER%-en sinadura egokia", + "ERROR": "OpenPGP errorea: %ERROR%" + }, + "POPUPS_FILTER": { + "TITLE_CREATE_FILTER": "Sortu iragazkia?", + "TITLE_EDIT_FILTER": "Eguneratu iragazkia?", + "LEGEND_CONDITIONS": "Baldintzak", + "LEGEND_ACTIONS": "Ekintzak", + "BUTTON_ADD_CONDITION": "Gehitu baldintza", + "SELECT_ACTION_MOVE_TO": "Mugitu hona", + "SELECT_ACTION_FORWARD_TO": "Birbidali honi", + "SELECT_ACTION_REJECT": "Errefusatu", + "SELECT_ACTION_VACATION_MESSAGE": "Opor mezua", + "SELECT_ACTION_DISCARD": "Baztertu", + "SELECT_FIELD_RECIPIENTS": "Hartzaileak (Nori edo CC)", + "SELECT_FIELD_HEADER": "Goiburua", + "SELECT_FIELD_BODY": "Gorputza", + "SELECT_FIELD_SIZE": "Tamaina", + "SELECT_TYPE_CONTAINS": "Barnean du", + "SELECT_TYPE_NOT_CONTAINS": "Barnean ez du", + "SELECT_TYPE_MATCHES": "Bat etortzeak (* eta ? onartzen dira)", + "SELECT_TYPE_NOT_MATCHES": "Bat ez etortzeak (* eta ? onartzen dira)", + "SELECT_TYPE_REGEXP": "Adierazpen erregularra", + "SELECT_TYPE_NOT_REGEXP": "Adierazpen erregularra", + "SELECT_TYPE_EQUAL_TO": "Berdina", + "SELECT_TYPE_NOT_EQUAL_TO": "Desberdina", + "SELECT_TYPE_OVER": "Gainean", + "SELECT_TYPE_UNDER": "Azpian", + "SELECT_TYPE_TEXT": "Testua", + "SELECT_TYPE_RAW": "Raw", + "SELECT_MATCH_ANY": "Bat etortzeak hurrengo EDOZEIN araurekin", + "SELECT_MATCH_ALL": "Bat etortzeak hurrengo arau GUZTIEKIN", + "MARK_AS_READ_LABEL": "Markatu irakurri bezala", + "REPLY_INTERVAL_LABEL": "Erantzuteko tartea (egunak)", + "KEEP_LABEL": "Mantenu", + "STOP_LABEL": "Ez geratu arazoak prozesatzeaz", + "VACATION_SUBJECT_LABEL": "Gaia (hautazkoa)", + "VACATION_MESSAGE_LABEL": "Mezua", + "VACATION_RECIPIENTS_LABEL": "Hartzaileak (komaz bananduta)", + "REJECT_MESSAGE_LABEL": "Errefusatu mezua", + "ALL_INCOMING_MESSAGES_DESC": "Iristen diren mezu guztiak" + }, + "POPUPS_SIEVE_SCRIPT": { + "TITLE_CREATE": "Sortu Scripta", + "TITLE_EDIT": "Editatu Scripta", + "BUTTON_ADD_FILTER": "Gehitu iragazkia", + "BUTTON_RAW_SCRIPT": "Erabili erabiltzailearen script pertsonal bat", + "CAPABILITY_LABEL": "Gaitasunak", + "CHANGES_NEED_TO_BE_SAVED_DESC": "Aldaketa hauek zerbitzarian gorde behar dira." + }, + "POPUPS_SYSTEM_FOLDERS": { + "TITLE_SYSTEM_FOLDERS": "Aukeratu sistemaren karpetak", + "SELECT_CHOOSE_ONE": "Aukeratu bat", + "SELECT_UNUSE_NAME": "Ez erabili", + "NOTIFICATION_SENT": "Ez duzu sistemako \"Bidalitakoak"\ karpeta hautatu mezuak bidali ondoren bertan ipintzeko.\nBidalitako mezua gorde nahi ez baduzu hautatu \"Ez erabili\" aukera.\n", + "NOTIFICATION_DRAFTS": "Ez duzu sistemako \"Zirriborroak\" karpeta hautatu mezuak editatu bitartean gordetzeko.", + "NOTIFICATION_SPAM": "Ez duzu sistemako \"Zabor-posta\" karpeta hautatu zabor-mezuak kokatzeko.\nMezu hauek betirako ezabatu nahi badituzu, aukeratu \"Ez erabili\" aukera.\n", + "NOTIFICATION_TRASH": "Ez duzu sistemako \"Zakarrontzia\" karpeta hautatu zabor-mezuak kokatzeko.\nMezu hauek betirako ezabatu nahi badituzu, aukeratu \"Ez erabili\" aukera.\n", + "NOTIFICATION_ARCHIVE": "Ez duzu sistemako \"Artxiboa\" karpeta hautatu artxibatutako mezuak gordetzeko." + }, + "TITLES": { + "MAILBOX": "Sarrera-ontzia", + "SETTINGS": "Ezarpenak" + }, + "UPLOAD": { + "ERROR_FILE_IS_TOO_BIG": "Fitxategia handiegia da", + "ERROR_FILE_PARTIALLY_UPLOADED": "Fitxategia partzialki kargatu da errore ezezagun batengatik", + "ERROR_NO_FILE_UPLOADED": "Ez da fitxategirik kargatu", + "ERROR_MISSING_TEMP_FOLDER": "Fitxategi tenporala falta da", + "ERROR_ON_SAVING_FILE": "Fitxategi-kargako errore ezezagun bat gertatu da", + "ERROR_FILE_TYPE": "Ez baliozko fitxategi-mota", + "ERROR_UNKNOWN": "Fitxategi-kargako errore ezezagun bat gertatu da" + }, + "EDITOR": { + "TEXT_SWITCHER_PLAIN_TEXT": "HTML <-> TESTUA", + "TEXT_SWITCHER_SOURCE": "HTML <-> JATORRIA" + }, + "SETTINGS_LABELS": { + "LABEL_PERSONAL_NAME": "Pertsonala", + "LABEL_GENERAL_NAME": "Orokorra", + "LABEL_CONTACTS_NAME": "Kontaktuak", + "LABEL_FOLDERS_NAME": "Karpetak", + "LABEL_ACCOUNTS_NAME": "Kontuak", + "LABEL_FILTERS_NAME": "Iragazkiak", + "LABEL_SECURITY_NAME": "Segurtasuna", + "LABEL_THEMES_NAME": "Gaiak" + }, + "SETTINGS_FILTERS": { + "LEGEND_FILTERS": "Iragazkiak", + "SIMPLE": "Arrunta", + "ADVANCED": "Aurretatua", + "BUTTON_ADD_SCRIPT": "Gehitu Scripta", + "SUBNAME_MOVE_TO": "Mugitu \"%FOLDER%\"-era", + "SUBNAME_FORWARD_TO": "Birbidali \"%EMAIL%\"-era", + "SUBNAME_REJECT": "Errefusatu", + "SUBNAME_VACATION_MESSAGE": "Opor-mezua", + "SUBNAME_DISCARD": "Baztertu" + }, + "SETTINGS_SECURITY": { + "LEGEND_SECURITY": "Segurtasuna", + "LABEL_AUTOLOGOUT": "Saioa automatikoki itxi", + "AUTOLOGIN_NEVER_OPTION_NAME": "Inoiz", + "AUTOLOGIN_MINUTES_OPTION_NAME": "%MINUTES% minututan", + "AUTOLOGIN_HOURS_OPTION_NAME": "%HOURS% ordutan" + }, + "SETTINGS_GENERAL": { + "LEGEND_GENERAL": "Orokorra", + "LANGUAGE": "Hizkuntza", + "IDENTITY": "Nortasuna", + "LAYOUT": "Diseinua", + "LAYOUT_NO_SPLIT": "Zatitu gabe", + "LAYOUT_VERTICAL_SPLIT": "Zatiketa bertikala", + "LAYOUT_HORIZONTAL_SPLIT": "Zatiketa horizontala", + "EDITOR": "Berezko testu editorea", + "EDITOR_HTML": "Html", + "EDITOR_PLAIN": "Formaturik gabea", + "VIEW_OPTIONS": "Ikusteko aukerak", + "USE_PREVIEW_PANE": "Erabili aurreikusteko panela", + "HIDE_DELETED_MESSAGES": "Ezkutatu ezabatzeko markatutako mezuak", + "USE_CHECKBOXES_IN_LIST": "Bistaratu kontrol laukiak zerrendan", + "USE_THREADS": "Erabili hariak", + "REPLY_SAME_FOLDER": "Kokatu erantzunak erantzundako mezuaren karpeta berean", + "SHOW_IMAGES": "Erakusti beti kanpoko irudiak mezuaren gorputzean", + "PREFER_HTML": "Lehenetsi HTMLa testu arruntari", + "PREFER_HTML_INFO": "Mezuak batzuetan bi formatuetan datoz. Aukera honekin HTML edo testu arrunt partea erakustea aukera dezakezu.", + "REMOVE_COLORS": "Kendu atzealdeko irudia eta testuaren kolorean mezuaren gorputzetik", + "MESSAGE_PER_PAGE": "Orrialdeko mezuak", + "MARK_MESSAGE_READ_AFTER": "Markatu mezua irakurrita ondoren", + "MSG_DEFAULT_ACTION": "Lehenetsitako ekintza", + "SECONDS": "segundu", + "NOTIFICATIONS": "Jakinarazpenak", + "SOUND_NOTIFICATION": "Soinu-jakinarazpena", + "CHROME_NOTIFICATION_DESC": "Erakutsi mezu berrien jakinarazpen popup-ak", + "CHROME_NOTIFICATION_DESC_DENIED": "(Nabigatzaileak blokeatuta)", + "LIST_INLINE_ATTACHMENTS": "Erakutsi sareko eranskinak zerrendan", + "LIST_SIMPLE_ATTACHMENTS": "Eranskinen zerrenda arrunta" + }, + "SETTINGS_CONTACTS": { + "LABEL_CONTACTS_AUTOSAVE": "Automatikoki gehitu hartzaileak helbide-liburura", + "LEGEND_CONTACTS_SYNC": "Urruneko Sinkronizazioa (CardDAV)", + "LABEL_CONTACTS_SYNC_ENABLE": "Gaitu urruneko sinkronizazioa", + "LABEL_CONTACTS_SYNC_SERVER": "Zerbitzaria", + "LABEL_CONTACTS_SYNC_AB_URL": "Helbide-liburuaren URL-a", + "LABEL_CONTACTS_SYNC_USER": "Erabiltzailea", + "SYNC_READ": "Irakurtzeko soilik" + }, + "SETTINGS_THEMES": { + "LEGEND_THEMES": "Gaiak", + "LABEL_BACKGROUND": "Atzealdeko irudiak", + "LABEL_FONTS": "Letra-tipoak", + "BUTTON_UPLOAD_BACKGROUND_IMAGE": "Kargatu atzealdeko irudia (JPG, PNG)", + "ERROR_FILE_IS_TOO_BIG": "Fitxategia handiegia da", + "ERROR_FILE_TYPE_ERROR": "Ez-baliozko fitxategi-mota (JPG eta PNG soilik)", + "ERROR_UNKNOWN": "Fitxategi-karga errore ezezagun bat gertatu da" + }, + "SETTINGS_FOLDERS": { + "LEGEND_FOLDERS": "Karpeta Zerrenda", + "BUTTON_CREATE": "Sortu Karpeta", + "BUTTON_SYSTEM": "Sistemak Karpetak", + "BUTTON_SUBSCRIBE": "Harpidetu", + "BUTTON_UNSUBSCRIBE": "Harpidetza kendu", + "LOADING_PROCESS": "Karpeta zerrenda eguneratzen", + "CREATING_PROCESS": "Karpeta sortzen", + "DELETING_PROCESS": "Karpeta ezabatzen", + "RENAMING_PROCESS": "Karpeta berrizendatzen", + "TO_MANY_FOLDERS_DESC_1": "Karpeta gehiegi dituzu!", + "TO_MANY_FOLDERS_DESC_2": "Beraien zati bat bakarrik erakutsi dugu, errendimendu arazoak saihesteko.", + "HELP_DELETE_FOLDER": "Ezabatu karpeta", + "HELP_SHOW_HIDE_FOLDER": "Erakutsi\/ezkutatu karpeta", + "HELP_CHECK_FOR_NEW_MESSAGES": "Bilatu\/ez bilatu mezu berriak", + "HIDE_UNSUBSCRIBED": "Ezkutatu harpidetzik gabeko karpetak", + "UNHIDE_KOLAB_FOLDERS": "Erakutsi Kolab karpetak", + "TYPE_CALENDAR": "Egutegia", + "TYPE_CONFIGURATION": "Ezarpenak", + "TYPE_CONTACTS": "Kontaktuak", + "TYPE_FILES": "Fitxategiak", + "TYPE_JOURNAL": "Egunerokoa", + "TYPE_NOTES": "Oharrak", + "TYPE_TASKS": "Atazak" + }, + "SETTINGS_ACCOUNTS": { + "LEGEND_ACCOUNTS": "Kontuak", + "LEGEND_IDENTITIES": "Nortasunak", + "LEGEND_ACCOUNTS_AND_IDENTITIES": "Kontuak eta Nortasunak", + "BUTTON_ADD_ACCOUNT": "Gehitu kontu bat", + "BUTTON_ADD_IDENTITY": "Gehitu identitate bat", + "LOADING_PROCESS": "Eguneratzen...", + "DEFAULT_IDENTITY_LABEL": "lehenetsia" + }, + "SETTINGS_OPENPGP": { + "BUTTON_IMPORT_KEY": "Inportatu Gakoa", + "BUTTON_GENERATE_KEY_PAIR": "Sortu Gako Parea", + "TITLE_PRIVATE": "Pribatua", + "TITLE_PUBLIC": "Publikoa", + "LABEL_ALLOW_DRAFT_AUTOSAVE": "Automatikoi gorde zirriborroa", + "GET_MAILVELOPE": "Lortu Mailvelope nabigatzaileko gehigarria." + }, + "SHORTCUTS_HELP": { + "LEGEND_SHORTCUTS_HELP": "Laster-teklen laguntza", + "TAB_MAILBOX": "Sarrera-ontzia", + "TAB_MESSAGE_LIST": "Mezuen zerrenda", + "TAB_MESSAGE_VIEW": "Mezuen ikuspegia", + "TAB_COMPOSE": "Prestatu", + "LABEL_OPEN_USER_DROPDOWN": "Ireki erabiltzailearen goitibeherako menua", + "LABEL_REPLY": "Erantzun", + "LABEL_REPLY_ALL": "Erantzun Denei", + "LABEL_FORWARD": "Birbidali", + "LABEL_FORWARD_MULTIPLY": "Birbidali eranskin moduan", + "LABEL_HELP": "Laguntza", + "LABEL_CHECK_ALL": "Aukeratu mezu guztiak", + "LABEL_DELETE": "Ezabatu", + "LABEL_OPEN_THREAD": "Ireki aukeratutako haria", + "LABEL_MOVE": "Mugitu", + "LABEL_READ": "Irakurri aukeratutako mezuak", + "LABEL_UNREAD": "Ez irakurri aukeratutako mezuak", + "LABEL_IMPORTANT": "Garrantzitsua, markatu aukeratutako mezuak", + "LABEL_CANCEL_SEARCH": "Ezeztatu bilaketa", + "LABEL_FULLSCREEN_ENTER": "Pantaila osoa (aurrebista panelaarekin)", + "LABEL_VIEW_MESSAGE_ENTER": "Ikusi mezua (aurrebista panelik gabe)", + "LABEL_SWITCH_TO_MESSAGE": "Fokuratu hautatutako mezua", + "LABEL_SWITCH_TO_FOLDER_LIST": "Fokuratu hautatutako mezua", + "LABEL_FULLSCREEN_TOGGLE": "Aldatu pantaila osoko modua", + "LABEL_BLOCKQUOTES_TOGGLE": "Aldatu mezuen aipuak", + "LABEL_THREAD_NEXT": "Hariko hurrengo mezua", + "LABEL_THREAD_PREV": "Hariko aurreko mezua", + "LABEL_EXIT_FULLSCREEN": "Irten pantaila osoko modutik", + "LABEL_CLOSE_MESSAGE": "Itxi mezua (aurrebista panelik gabe)", + "LABEL_SWITCH_TO_LIST": "Itzuli fokua mezuen zerrendara", + "LABEL_OPEN_COMPOSE_POPUP": "Ireki prestatzeko popup-a", + "LABEL_MINIMIZE_COMPOSE_POPUP": "Txikitu prestatzeko popup-a", + "LABEL_OPEN_IDENTITIES_DROPDOWN": "Ireki identitateen goitibeherako menua, + "LABEL_SAVE_MESSAGE": "Gorde mezua", + "LABEL_SEND_MESSAGE": "Bidali mezua", + "LABEL_CLOSE_COMPOSE": "Itxi mezua" + }, + "NOTIFICATIONS": { + "REQUEST_ERROR": "Eskaerak huts egin du", + "REQUEST_ABORTED": "Eskaera bertan behera utzi da", + "REQUEST_TIMEOUT": "Eskaera denboraz kantpo", + "INVALID_TOKEN": "Ez-baliozko tokena", + "AUTH_ERROR": "Autentikazio errorea", + "CONNECTION_ERROR": "Ezin da zerbitzarira konektatu", + "DOMAIN_ALREADY_EXISTS": "Domeinua jadanik existitzen da", + "DOMAIN_NOT_ALLOWED": "Domeinu ez baimendua", + "ACCOUNT_NOT_ALLOWED": "Kontu ez baimendua", + "CONTACTS_SYNC_ERROR": "Kontaktu sinkronizazio errorea", + "CANT_GET_MESSAGE_LIST": "Ezin da mezuen zerrenda eskuratu", + "CANT_GET_MESSAGE": "Ezin da mezua eskuratu", + "CANT_DELETE_MESSAGE": "Ezin da mezua ezabatu", + "CANT_COPY_MESSAGE": "Ezin da mezua kopiatu", + "CANT_MOVE_MESSAGE": "Ezin da mezua mugitu", + "CANT_SAVE_MESSAGE": "Ezin da mezua gorde", + "CANT_SEND_MESSAGE": "Ezin da mezua bidali", + "INVALID_RECIPIENTS": "Ez-baliozko hartzaileak", + "CANT_SAVE_FILTERS": "Ezin dira iragazkiak gorde", + "CANT_GET_FILTERS": "Ezin dira iragazkiak eskuratu", + "CANT_ACTIVATE_FILTERS_SCRIPT": "Ezin dira iragazki-scriptak aktibatu", + "CANT_DELETE_FILTERS_SCRIPT": "Ezin dira iragazki-scriptak ezabatu", + "FILTERS_ARE_NOT_CORRECT": "Iragazkiak ez dira zuzenak", + "CANT_CREATE_FOLDER": "Ezin da karpeta sortu", + "CANT_RENAME_FOLDER": "Ezin da karpeta berrizendatu", + "CANT_DELETE_FOLDER": "Ezin da karpeta ezabatu", + "CANT_DELETE_NON_EMPTY_FOLDER": "Ezin da hutsi ez dagoen direktorioa ezabatu", + "CANT_SUBSCRIBE_FOLDER": "Ezin da karpetara harpidetu", + "CANT_UNSUBSCRIBE_FOLDER": "Ezin da karpetatik harpidetza kendu", + "DEMO_SEND_MESSAGE_ERROR": "Segurtasunagatik, kontua honek ezin du kanpoko e-mail helbideetara mezurik bidali!", + "DEMO_ACCOUNT_ERROR": "Segurtasunagatik, kontu hau ez dago ahalbideratuta ekintza honetarako!", + "ACCOUNT_ALREADY_EXISTS": "Kontua jada existitzen da", + "ACCOUNT_DOES_NOT_EXIST": "Kontua ez da existitzen", + "ACCOUNT_SWITCH_FAILED": "\"%EMAIL%\" kontura aldaketak akatsa eman du", + "MAIL_SERVER_ERROR": "Errore bat gertatu da posta zebritzarira sartzean", + "INVALID_INPUT_ARGUMENT": "Ez-naliozko sarrera-argumentua", + "UNKNOWN_ERROR": "Errore ezezaguna" + } +} From ac5faa90ee19ca9baf29a933a040560591b7b0a5 Mon Sep 17 00:00:00 2001 From: Olloki Date: Thu, 24 Nov 2022 12:41:59 +0100 Subject: [PATCH 102/114] Basque static.json Basque translation of static.json --- snappymail/v/0.0.0/app/localization/eu/static.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 snappymail/v/0.0.0/app/localization/eu/static.json diff --git a/snappymail/v/0.0.0/app/localization/eu/static.json b/snappymail/v/0.0.0/app/localization/eu/static.json new file mode 100644 index 000000000..1ea35b0d2 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/eu/static.json @@ -0,0 +1,9 @@ +{ + "BACK_LINK": "Birkargatu", + "NO_SCRIPT_TITLE": "JavaScript beharrezkoa da aplikazio honetarako.", + "NO_SCRIPT_DESC": "Zure nabigatzaileak ez du JavaScript onartzen.\nMesedez gaitu JavaScript zure nabigatzailearen ezarpenetan eta saiatu berriro.\n", + "NO_COOKIE_TITLE": "Cookieak onartzea behar da aplikazio honetarako.", + "NO_COOKIE_DESC": "Zure nabigatzaileak ez ditu Cookieak onartzen.\nMesedez gaitu Cookieak zure nabigatzailearen ezarpenetan eta saiatu berriro.\n", + "BAD_BROWSER_TITLE": "Zure nabigatzailea ez dago eguneratuta.", + "BAD_BROWSER_DESC": "Aplikazio honen ezaugarri gutziak erabiltzeko,\ndeskargatu eta instalatu ondo nabigatzaileetako bat:\n" +} From b29055a825452621d685641ad4539fd26aacf7d3 Mon Sep 17 00:00:00 2001 From: Olloki Date: Thu, 24 Nov 2022 12:51:29 +0100 Subject: [PATCH 103/114] Basque relativetimeformat.js Basque translation on relativetimeformat.js --- .../app/localization/eu/relativetimeformat.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 snappymail/v/0.0.0/app/localization/eu/relativetimeformat.js diff --git a/snappymail/v/0.0.0/app/localization/eu/relativetimeformat.js b/snappymail/v/0.0.0/app/localization/eu/relativetimeformat.js new file mode 100644 index 000000000..29f66890d --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/eu/relativetimeformat.js @@ -0,0 +1,75 @@ +{ + long: { + year: { + past: { + one: "duela urtebete", + other: "duela {0} urte" + }, + future: { + one: "urtebete barru", + other: "{0} urte barru" + } + }, + month: { + past: { + one: "duela hilabete", + other: "duela {0} hilabete" + }, + future: { + one: "hilabete barru", + other: "{0} hilabete barru" + } + }, + week: { + past: { + one: "duela astebete", + other: "duela {0} aste" + }, + future: { + one: "astebete barru", + other: "{0} aste barru" + } + }, + day: { + past: { + one: "duela egun bat", + other: "duela {0} egun" + }, + future: { + one: "egun bat barru", + other: "{0} egun barru" + } + }, + hour: { + past: { + one: "duela ordubete", + other: "duela {0} ordu" + }, + future: { + one: "ordubete barru", + other: "{0} ordu barru" + } + }, + minute: { + past: { + one: "duela minutu {0}", + other: "duela {0} minutu" + }, + future: { + one: "minutu {0} barru", + other: "{0} minutu barru" + } + }, + second: { + past: { + one: "duela segundo {0}", + other: "duela {0} segundo" + }, + future: { + one: "segundo {0} barru", + other: "{0} segundo barru" + } + } + }, + plural: n => 1 == n ? 'one' : 'other' +} From 3948b119989568bfe1c9ae219914e1321879844a Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 13:09:47 +0100 Subject: [PATCH 104/114] Support cPanel #697 --- _include.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/_include.php b/_include.php index 9f16ed888..be4231442 100644 --- a/_include.php +++ b/_include.php @@ -29,6 +29,15 @@ */ //define('APP_DATA_FOLDER_PATH', dirname(__DIR__) . '/snappymail-data/'); //define('APP_DATA_FOLDER_PATH', '/var/external-snappymail-data-folder/'); +if (isset($_ENV['CPANEL']) && isset($_ENV['TMPDIR'])) { + define('APP_DATA_FOLDER_PATH', $_ENV['TMPDIR'] . '/snappymail/'); +/* + $_ENV['USER'] => + $_ENV['HOME'] => /home/ + $_ENV['TMPDIR'] => /home//tmp + $_ENV['REAL_DBOWNER'] => +*/ +} /** * Additional configuration file name From 7246dd083f974694f5e9f4ff82406793e3aa4b17 Mon Sep 17 00:00:00 2001 From: Olloki Date: Thu, 24 Nov 2022 13:23:59 +0100 Subject: [PATCH 105/114] Basque admin.json Basque translated admin.json --- .../v/0.0.0/app/localization/eu/admin.json | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 snappymail/v/0.0.0/app/localization/eu/admin.json diff --git a/snappymail/v/0.0.0/app/localization/eu/admin.json b/snappymail/v/0.0.0/app/localization/eu/admin.json new file mode 100644 index 000000000..696283b52 --- /dev/null +++ b/snappymail/v/0.0.0/app/localization/eu/admin.json @@ -0,0 +1,180 @@ +{ + "GLOBAL": { + "ADD": "Gehitu", + "CLOSE": "Itxi", + "SAVE": "Gorde", + "TEST": "Probatu", + "UPDATE": "Eguneratu", + "USERNAME": "Erabiltzailea", + "PASSWORD": "Pasahitza", + "CANCEL": "Ezeztatu" + }, + "LOGIN": { + "LABEL_LOGIN": "Login", + "LABEL_PASSWORD": "Pasahitza", + "LABEL_TOTP": "TOTP kodea", + "BUTTON_LOGIN": "Administradore panelean saioa hasi" + }, + "TOP_PANEL": { + "LABEL_ADMIN_PANEL": "Administradore Panela" + }, + "SETTINGS_LABELS": { + "LABEL_GENERAL_NAME": "Orokorra", + "LABEL_LOGIN_NAME": "Login", + "LABEL_BRANDING_NAME": "Marka", + "LABEL_CONTACTS_NAME": "Kontaktuak", + "LABEL_DOMAINS_NAME": "Domeinuak", + "LABEL_SECURITY_NAME": "Segurtasuna", + "LABEL_PACKAGES_NAME": "Gehigarriak", + "LABEL_CONFIG_NAME": "Ezarpenak", + "LABEL_ABOUT_NAME": "Honi buruz" + }, + "TAB_GENERAL": { + "LEGEND_INTERFACE": "Interfazea", + "LABEL_LANGUAGE": "Hizkuntza", + "LABEL_LANGUAGE_ADMIN": "Hizkuntza (admin)", + "LABEL_THEME": "Gaia", + "LABEL_ALLOW_LANGUAGES_ON_SETTINGS": "Baimendu hizkuntza aukeratzea ezarpenen pantailan", + "LABEL_ALLOW_THEMES_ON_SETTINGS": "Baimendu gaia aukeratzea ezarpenen pantailan", + "LABEL_ALLOW_BACKGROUND_ON_SETTINGS": "Baimendu atzealdeko irudia aukeratzea ezarpenen pantailan", + "LABEL_SHOW_THUMBNAILS": "Erakutsi miniaturak (gehigarriak)", + "LEGEND_MAIN": "Nagusia", + "LABEL_ATTACHMENT_SIZE_LIMIT": "Eranskinen tamaina-muga", + "LABEL_ALLOW_ADDITIONAL_ACCOUNTS": "Onartu kontu gehigarriak", + "LABEL_ALLOW_IDENTITIES": "Onartu nortasun gehigarriak", + "ALERT_DATA_ACCESS": "Datu karpeta eskuragarri dago. Mesedez, konfigruatu zure web zerbitzaria datuen karpeta kanpoko sarbidetik ezkutatzeko. Irakurri gehiago hemen:", + "ALERT_WARNING": "Kontuz!", + "HTML_ALERT_WEAK_PASSWORD": "Lehenetsitako administradore pasahitza erabiltzen ari zara.\n
    \nSegurtasunagatik, mesedez,\naldatu<\/a><\/strong>\npasahitza beste zerbaitetara orain.\n" + }, + "TAB_LOGIN": { + "LEGEND_LOGIN_SCREEN": "Saio hasiera pantaila", + "LABEL_DEFAULT_DOMAIN": "Lehenetsitako Domeinua", + "LABEL_DETERMINE_USER_DOMAIN": "Saiatu zehazten erabiltzailearen domeinua", + "LABEL_ALLOW_LANGUAGES_ON_LOGIN": "Onartu hizkuntza hautaketa saio-hasiera pantailan", + "LABEL_DETERMINE_USER_LANGUAGE": "Saiatu zehazten erabiltzailearen hizkuntza" + }, + "TAB_BRANDING": { + "LEGEND_BRANDING": "Marka", + "LABEL_PAGE_TITLE": "Orriaren izenburua", + "LABEL_LOADING_DESCRIPTION": "Deskribapena Kargatzen", + "LABEL_FAVICON_URL": "Favicon" + }, + "TAB_CONTACTS": { + "LEGEND_CONTACTS": "Kontaktuak", + "LEGEND_STORAGE": "Biltegia (PDO)", + "LABEL_ENABLE_CONTACTS": "Gaitu kontaktuak", + "LABEL_ALLOW_SYNC": "Onartu kontaktuen sinkronizazioa (kanpoko CardDAV zerbitzaria)", + "LABEL_STORAGE_TYPE": "Mota", + "LABEL_STORAGE_DSN": "Dsn", + "LABEL_STORAGE_USER": "Erabiltzailea", + "LABEL_STORAGE_PASSWORD": "Pasahitza", + "ALERT_NOTICE": "Adi!", + "HTML_ALERT_DO_NOT_USE_THIS_DATABASE": "Ez erabili datubase hau erabiltzaile aktibo koputu handiarekin.", + "HTML_ALERT_DOES_NOT_SUPPORTED": "Zure sistemak ez ditu kontaktuak onartzen.\n
    \nPDO (SQLite \/ MySQL \/ PostgreSQL)<\/strong> gehigarria instalatu edo gaitu behar duzu zure zerbitzarian.\n" + }, + "TAB_DOMAINS": { + "LEGEND_DOMAINS": "Domeinuak", + "BUTTON_ADD_DOMAIN": "Gehitu Domeinua", + "BUTTON_ADD_ALIAS": "gehitu Aliasa", + "DELETE_ARE_YOU_SURE": "Ziur zaude?", + "HTML_DOMAINS_HELPER": "Webmail-a sartzera baimenduta dagoen domeinu zerrenda.\n
    \nKlikatu izenean domeinua konfiguratzeko.\n" + }, + "TAB_SECURITY": { + "LEGEND_SECURITY": "Segurtasuna", + "LABEL_USE_IMAGE_PROXY": "Erabili proxy lokala kanpoko irudientzat", + "LABEL_ALLOW_OPEN_PGP": "Baimendu OpenPGP", + "LABEL_SHOW_PHP_INFO": "Erakutsi PHP informazioa", + "LEGEND_ADMIN_PANEL_ACCESS_CREDENTIALS": "Admin Panelerako Sarbide Kredentzialak", + "LABEL_CURRENT_PASSWORD": "Uneko pasahitza", + "LABEL_NEW_PASSWORD": "Pasahitz berria", + "LABEL_REPEAT_PASSWORD": "Errepikatu", + "LABEL_REQUIRE_VERIFICATION": "SSL ziurtagiriaren baliozkotzea eskatu", + "LABEL_ALLOW_SELF_SIGNED": "Onartu norberak sinatutako ziurtagiriak" + }, + "TAB_PACKAGES": { + "LABEL_ENABLE_PLUGINS": "Onartu gehigarriak", + "LEGEND_AVAILABLE_FOR_UPDATE": "Eguneratzeko eskuragarri", + "LEGEND_AVAILABLE_FOR_INSTALLATION": "Instalatzeko eskuragarri", + "LEGEND_INSTALLED_PACKAGES": "Instalatutako Gehigarriak", + "ALERT_CANNOT_ACCESS_REPOSITORY": "Ezin da sartu biltegira une honetan." + }, + "TAB_ABOUT": { + "LEGEND_ABOUT": "Honi buruz", + "LABEL_TAG_HINT": "Weban oinarritutako email-bezero erraz, moderno eta azkarra", + "LABEL_ALL_RIGHTS_RESERVED": "Eskubide guztiak gordeak.", + "HINT_READ_CHANGE_LOG": "Mesedez irakurri aldaketen informea eguneratu aurretik.", + "HINT_IS_UP_TO_DATE": "SnappyMail eguneratuta dago.", + "HTML_NEW_VERSION": "%VERSION% bertsio berria eskuragarri.", + "LABEL_UPDATING": "Eguneratzen", + "LABEL_CHECKING": "Eguneraketak dauden begiratzen", + "BUTTON_RELEASES": "Bertsioak" + }, + "POPUPS_DOMAIN_ALIAS": { + "TITLE_ADD_DOMAIN_ALIAS": "Gehitu Aliasa", + "LABEL_ALIAS": "Alias", + "LABEL_DOMAIN": "Domeinua" + }, + "POPUPS_DOMAIN": { + "TITLE_ADD_DOMAIN": "Gehitu Domeinua", + "TITLE_ADD_DOMAIN_WITH_NAME": "Gehitu Domeinua \"%NAME%\"", + "TITLE_EDIT_DOMAIN": "Editatu Domeinua \"%NAME%\"", + "LABEL_NAME": "Izena", + "NAME_HELPER": "komodina onartuta", + "LABEL_IMAP": "IMAP", + "LABEL_SMTP": "SMTP", + "LABEL_SIEVE": "SIEVE", + "LABEL_SERVER": "Zerbitzaria", + "LABEL_PORT": "Portua", + "LABEL_SECURE": "Segurtasuna", + "LABEL_WHITE_LIST": "Zerrenda Zuria", + "SECURE_OPTION_NONE": "Bat ere ez", + "SECURE_OPTION_SSL": "SSL\/TLS", + "SECURE_OPTION_STARTTLS": "STARTTLS", + "LABEL_ALLOW_SIEVE_SCRIPTS": "Onartu sieve scriptak", + "LABEL_USE_SHORT_LOGIN": "Erabili login motza", + "LABEL_USE_AUTH": "Erabili autentikazioa", + "LABEL_SET_SENDER": "Erabili logina bidaltzaile bezala", + "LABEL_USE_PHP_MAIL": "Erabili php mail() funtzioa", + "BUTTON_WHITE_LIST": "Zerrenda Zuria", + "BUTTON_SIEVE_CONFIGURATION": "Sieve ezarpenak", + "BUTTON_BACK_TO_IMAP": "Itzuli IMAP ezarpenetara", + "BUTTON_BACK": "Atzera", + "BUTTON_UPDATE": "Eguneratu", + "NEW_DOMAIN_DESC": "Domeinu honen konfigurazioak
    %NAME%<\/i> email kontuarekin lan egitea ahalbideratuko dizu.", + "WHITE_LIST_ALERT": "Webmailak sartzeko baimena duen domeinuko erabiltzaileen zerrenda.\nErabili hutsunea banatzaile bezala.\n" + }, + "POPUPS_PLUGIN": { + "TITLE_PLUGIN": "Gehigarria", + "DESC_NOTHING_TO_CONFIGURE": "Ez dago kongiguratzeko ezer" + }, + "POPUPS_ASK": { + "BUTTON_YES": "Bai", + "BUTTON_NO": "Ez", + "DESC_WANT_CLOSE_THIS_WINDOW": "Ziur zaude leiho hau itxi nahi duzula?" + }, + "POPUPS_LANGUAGES": { + "TITLE_LANGUAGES": "Aukeratu zure hizkuntza" + }, + "HINTS": { + "BETA": "beta", + "WARNING": "Kontuz!" + }, + "ERRORS": { + "DOMAIN_ALREADY_EXISTS": "Domeinua jadanik existizen da", + "UNKNOWN_ERROR": "Errore ezezaguna" + }, + "NOTIFICATIONS": { + "REQUEST_ERROR": "Eskaerak huts egin du", + "REQUEST_ABORTED": "Eskaera bertan behera utzi da", + "REQUEST_TIMEOUT": "Eskaeraren denbora-muga gainditu da", + "INVALID_TOKEN": "Ez-baliozko tokena", + "AUTH_ERROR": "Autentikazioak huts egin du", + "DOMAIN_ALREADY_EXISTS": "Domeinua jadanik existitzen da", + "CANT_SAVE_PLUGIN_SETTINGS": "Ezin dira ezarpenak gorde", + "CANT_INSTALL_PACKAGE": "Akatsa gehigarria instalatzean", + "CANT_DELETE_PACKAGE": "Akatsa gehigarria kentzean", + "INVALID_PLUGIN_PACKAGE": "Ez-baliozko gehigarri paketea", + "UNSUPPORTED_PLUGIN_PACKAGE": "Gehigarri paketea ez da onartzen", + "UNKNOWN_ERROR": "Errore ezezaguna" + } +} From b44da10e27d9b6e7e1cd03fd9f7566421bf6cc96 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 14:30:09 +0100 Subject: [PATCH 106/114] Better cPanel integration #697 --- _include.php | 9 --------- snappymail/v/0.0.0/cpanel.php | 27 +++++++++++++++++++++++++++ snappymail/v/0.0.0/include.php | 16 +++++++++++++--- 3 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 snappymail/v/0.0.0/cpanel.php diff --git a/_include.php b/_include.php index be4231442..9f16ed888 100644 --- a/_include.php +++ b/_include.php @@ -29,15 +29,6 @@ */ //define('APP_DATA_FOLDER_PATH', dirname(__DIR__) . '/snappymail-data/'); //define('APP_DATA_FOLDER_PATH', '/var/external-snappymail-data-folder/'); -if (isset($_ENV['CPANEL']) && isset($_ENV['TMPDIR'])) { - define('APP_DATA_FOLDER_PATH', $_ENV['TMPDIR'] . '/snappymail/'); -/* - $_ENV['USER'] => - $_ENV['HOME'] => /home/ - $_ENV['TMPDIR'] => /home//tmp - $_ENV['REAL_DBOWNER'] => -*/ -} /** * Additional configuration file name diff --git a/snappymail/v/0.0.0/cpanel.php b/snappymail/v/0.0.0/cpanel.php new file mode 100644 index 000000000..bcdd7bb0c --- /dev/null +++ b/snappymail/v/0.0.0/cpanel.php @@ -0,0 +1,27 @@ +Set('plugins', 'enable', true); + $oConfig->Set('plugins', 'enabled_list', \implode(',', \array_unique($aList))); + $oConfig->Set('login', 'default_domain', 'cpanel'); + $oConfig->Save(); + + $sFile = APP_PRIVATE_DATA.'domains/cpanel.json'; + if (!file_exists($sFile)) { + $config = json_decode(file_get_contents(__DIR__ . '/app/domains/default.json'), true); + $config['IMAP']['shortLogin'] = true; + $config['SMTP']['shortLogin'] = true; + file_put_contents($sFile, json_encode($config, JSON_PRETTY_PRINT)); + } + + $_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = $asApi; +} diff --git a/snappymail/v/0.0.0/include.php b/snappymail/v/0.0.0/include.php index 3823e7d54..d655c23e7 100644 --- a/snappymail/v/0.0.0/include.php +++ b/snappymail/v/0.0.0/include.php @@ -71,16 +71,21 @@ define('APP_PRIVATE_DATA_NAME', $sPrivateDataFolderInternalName ?: '_default_'); unset($sPrivateDataFolderInternalName); if (!defined('APP_DATA_FOLDER_PATH')) { - $sCustomDataPath = function_exists('__get_custom_data_full_path') ? rtrim(trim(__get_custom_data_full_path()), '\\/') : $sCustomDataPath; + // cPanel https://github.com/the-djmaze/snappymail/issues/697 + if (!empty($_ENV['CPANEL']) && isset($_ENV['TMPDIR'])) { + $sCustomDataPath = $_ENV['TMPDIR'] . '/snappymail'; + } else { + $sCustomDataPath = function_exists('__get_custom_data_full_path') ? rtrim(trim(__get_custom_data_full_path()), '\\/') : $sCustomDataPath; + } define('APP_DATA_FOLDER_PATH', strlen($sCustomDataPath) ? $sCustomDataPath.'/' : APP_INDEX_ROOT_PATH.'data/'); - unset($sCustomDataPath); } +unset($sCustomDataPath); if (!defined('APP_CONFIGURATION_NAME')) { define('APP_CONFIGURATION_NAME', function_exists('__get_additional_configuration_name') ? trim(__get_additional_configuration_name()) : $sCustomConfiguration); - unset($sCustomConfiguration); } +unset($sCustomConfiguration); $sData = is_file(APP_DATA_FOLDER_PATH.'DATA.php') ? file_get_contents(APP_DATA_FOLDER_PATH.'DATA.php') : ''; define('APP_PRIVATE_DATA', APP_DATA_FOLDER_PATH.'_data_'.($sData ? md5($sData) : '').'/'.APP_PRIVATE_DATA_NAME.'/'); @@ -158,6 +163,11 @@ if (!ini_get('zlib.output_compression') && !ini_get('brotli.output_compression') } } +// cPanel https://github.com/the-djmaze/snappymail/issues/697 +if (!empty($_ENV['CPANEL']) && !is_dir(APP_PLUGINS_PATH.'login-remote')) { + require __DIR__ . '/cpanel.php'; +} + if (class_exists('RainLoop\\Api')) { RainLoop\Api::Handle(); // NextCloud/OwnCloud? From 3594b344fd2c05f62ef6cf19790101817060565c Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 14:31:57 +0100 Subject: [PATCH 107/114] Bugfix: domain "hostname".json shortLogin failed --- snappymail/v/0.0.0/setup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/setup.php b/snappymail/v/0.0.0/setup.php index bc9ccce77..068fdfa2f 100644 --- a/snappymail/v/0.0.0/setup.php +++ b/snappymail/v/0.0.0/setup.php @@ -161,8 +161,8 @@ if (defined('APP_VERSION')) $sFile = APP_PRIVATE_DATA.'domains/'.$sName.'.json'; if (!file_exists($sFile) && !file_exists(APP_PRIVATE_DATA.'domains/'.$sName.'.ini')) { $config = json_decode(file_get_contents(__DIR__ . '/app/domains/default.json'), true); - $config['imapShortLogin'] = true; - $config['smtpShortLogin'] = true; + $config['IMAP']['shortLogin'] = true; + $config['SMTP']['shortLogin'] = true; file_put_contents($sFile, json_encode($config, JSON_PRETTY_PRINT)); } } From 6c8d8d597bfd6fb59e1cee8500541a35912819d4 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 14:43:03 +0100 Subject: [PATCH 108/114] v2.22.2 --- .docker/release/files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 8 +++++++- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- integrations/nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 29fefb610..1f5ed1023 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -289,5 +289,5 @@ dev_email = "" dev_password = "" [version] -current = "2.22.1" +current = "2.22.2" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index c93ee467e..f9c1d1058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## 2.22.0 – 2022-11-23 +## 2.22.2 – 2022-11-24 + +### Added +- Support cPanel #697 + + +## 2.22.1 – 2022-11-23 ### Added - AddressBookInterface::GetContactByEmail() to support sender image/avatar extension diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 4b44b7fa3..a182909e6 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.22.1. +This app packages SnappyMail 2.22.2. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 88134b8e5..d1d70f1ad 100644 --- a/integrations/cloudron/Dockerfile +++ b/integrations/cloudron/Dockerfile @@ -4,7 +4,7 @@ RUN mkdir -p /app/code WORKDIR /app/code # If you change the extraction below, be sure to test on scaleway -VERSION=2.22.1 +VERSION=2.22.2 RUN wget https://github.com/the-djmaze/snappymail/releases/download/v${VERSION}/snappymail-${VERSION}.zip -O /tmp/snappymail.zip && \ unzip /tmp/snappymail.zip -d /app/code && \ rm /tmp/snappymail.zip && \ diff --git a/integrations/nextcloud/snappymail/appinfo/info.xml b/integrations/nextcloud/snappymail/appinfo/info.xml index ee192e6f0..5f121a00f 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.22.1 + 2.22.2 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli Date: Thu, 24 Nov 2022 15:36:02 +0100 Subject: [PATCH 109/114] Fix cPanel user initialization --- .../libraries/RainLoop/Plugins/Manager.php | 20 +++++++++++++------ snappymail/v/0.0.0/cpanel.php | 6 ++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php index e89b88668..37bcbe7c5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php @@ -41,15 +41,11 @@ class Manager if (\strlen($sList)) { $aList = \SnappyMail\Repository::getEnabledPackagesNames(); foreach ($aList as $i => $sName) { - $oPlugin = $this->CreatePluginByName($sName); - if ($oPlugin) { - $oPlugin->Init(); - $this->aPlugins[] = $oPlugin; - } else { + if (!$this->loadPlugin($sName)) { unset($aList[$i]); } } - $aList = \implode(',', \array_unique($aList)); + $aList = \implode(',', \array_keys($this->aPlugins)); if ($sList != $aList) { $oConfig->Set('plugins', 'enabled_list', $aList); $oConfig->Save(); @@ -58,6 +54,18 @@ class Manager } } + public function loadPlugin(string $sName) : bool + { + if (!isset($this->aPlugins[$sName])) { + $oPlugin = $this->CreatePluginByName($sName); + if ($oPlugin) { + $oPlugin->Init(); + $this->aPlugins[$sName] = $oPlugin; + } + } + return isset($this->aPlugins[$sName]); + } + protected static function getPluginPath(string $sName) : ?string { $sPath = APP_PLUGINS_PATH.$sName; diff --git a/snappymail/v/0.0.0/cpanel.php b/snappymail/v/0.0.0/cpanel.php index bcdd7bb0c..fb1e63cda 100644 --- a/snappymail/v/0.0.0/cpanel.php +++ b/snappymail/v/0.0.0/cpanel.php @@ -23,5 +23,11 @@ if (defined('APP_PLUGINS_PATH') && !empty($_ENV['CPANEL']) && !is_dir(APP_PLUGIN file_put_contents($sFile, json_encode($config, JSON_PRETTY_PRINT)); } +// \RainLoop\Api::Actions()->Plugins()->loadPlugin('login-remote'); + if (!isset($_GET['installed'])) { + \header('Location: ?RemoteAutoLogin&installed'); + exit; + } + $_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = $asApi; } From 82d5d3417461ebe8bf0ebc0ddc90a9a77304054e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 16:44:55 +0100 Subject: [PATCH 110/114] Resolve #704 --- plugins/nextcloud/js/messagelist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nextcloud/js/messagelist.js b/plugins/nextcloud/js/messagelist.js index 13feda12d..5b5d27b43 100644 --- a/plugins/nextcloud/js/messagelist.js +++ b/plugins/nextcloud/js/messagelist.js @@ -6,7 +6,7 @@ let view = e.detail; view.nextcloudSaveMsgs = () => { view.messageList.hasChecked() - && rl.ncFiles.selectFolder().then(folder => { + && rl.nextcloud.selectFolder().then(folder => { folder && view.messageList.forEach(msg => { msg.checked() && rl.pluginRemoteRequest( (iError, data) => { From 2634edd842a45ff9d3c8e5a5d3a9a34ba59b200c Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 24 Nov 2022 16:57:37 +0100 Subject: [PATCH 111/114] Check for cPanel $_ENV['REMOTE_TEMP_USER'] --- plugins/login-remote/index.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/login-remote/index.php b/plugins/login-remote/index.php index cc2dfce52..6c7a2596d 100644 --- a/plugins/login-remote/index.php +++ b/plugins/login-remote/index.php @@ -62,11 +62,16 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin public function FilterLoginCredentials(&$sEmail, &$sLogin, &$sPassword) { // cPanel https://github.com/the-djmaze/snappymail/issues/697 - if (static::$login/* && $sLogin == $_SERVER['REMOTE_USER']*/) { - $iPos = \strpos($sPassword, '[::cpses::]'); - if ($iPos) { - $sKey = \substr($sPassword, 0, $iPos); - $sLogin = $_SERVER['REMOTE_USER'] . '/' . \substr($sPassword, 0, $iPos); +// && !empty($_ENV['CPANEL']) + if (static::$login/* && $sLogin == $_ENV['REMOTE_USER']*/) { + if (empty($_ENV['REMOTE_TEMP_USER'])) { + $iPos = \strpos($sPassword, '[::cpses::]'); + if ($iPos) { + $_ENV['REMOTE_TEMP_USER'] = \substr($sPassword, 0, $iPos); + } + } + if (!empty($_ENV['REMOTE_TEMP_USER'])) { + $sLogin = $_ENV['REMOTE_USER'] . '/' . $_ENV['REMOTE_TEMP_USER']; } } } From dee6f6af7e57ccafaffca87a0bccfc55d4ccd03f Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 25 Nov 2022 08:19:44 +0100 Subject: [PATCH 112/114] Added config logs.path and cache.path to improve custom data structure. Benefits cPanel integration #697 --- snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php | 9 ++++++--- .../0.0.0/app/libraries/RainLoop/Config/Application.php | 4 ++++ snappymail/v/0.0.0/cpanel.php | 2 ++ snappymail/v/0.0.0/include.php | 8 ++++---- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 1c4432568..d9913e934 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -509,7 +509,10 @@ class Actions switch (true) { default: case $bForceFile: - $oDriver = new \MailSo\Cache\Drivers\File(APP_PRIVATE_DATA . 'cache', $sKey); + $oDriver = new \MailSo\Cache\Drivers\File( + \trim($this->oConfig->Get('cache', 'path', APP_PRIVATE_DATA . 'cache')), + $sKey + ); break; case ('APCU' === $sDriver) && @@ -566,8 +569,8 @@ class Actions if ($this->oConfig->Get('logs', 'auth_logging', false)) { // $this->oLoggerAuth->SetLevel(\LOG_WARNING); - $sAuthLogFileFullPath = \APP_PRIVATE_DATA . 'logs/' . $this->compileLogFileName( - $this->oConfig->Get('logs', 'auth_logging_filename', '')); + $sAuthLogFileFullPath = \trim($this->oConfig->Get('logs', 'path', \APP_PRIVATE_DATA . 'logs')) + . '/' . $this->compileLogFileName($this->oConfig->Get('logs', 'auth_logging_filename', '')); $sLogFileDir = \dirname($sAuthLogFileFullPath); if (!\is_dir($sLogFileDir)) { \mkdir($sLogFileDir, 0755, true); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 503e14e44..4967825c1 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -283,6 +283,8 @@ Values: 'enable' => array(false, 'Enable logging'), + 'path' => array('', 'Path where log files will be stored'), + 'level' => array(4, 'Log messages of set RFC 5424 section 6.2.1 Severity level and higher (0 = highest, 7 = lowest). 0 = Emergency 1 = Alert @@ -343,6 +345,8 @@ Examples: Enables caching in the system'), + 'path' => array('', 'Path where cache files will be stored'), + 'index' => array('v1', 'Additional caching key. If changed, cache is purged'), 'fast_cache_driver' => array('files', 'Can be: files, APCU, memcache, redis (beta)'), diff --git a/snappymail/v/0.0.0/cpanel.php b/snappymail/v/0.0.0/cpanel.php index fb1e63cda..dabb72ff9 100644 --- a/snappymail/v/0.0.0/cpanel.php +++ b/snappymail/v/0.0.0/cpanel.php @@ -13,6 +13,8 @@ if (defined('APP_PLUGINS_PATH') && !empty($_ENV['CPANEL']) && !is_dir(APP_PLUGIN $oConfig->Set('plugins', 'enable', true); $oConfig->Set('plugins', 'enabled_list', \implode(',', \array_unique($aList))); $oConfig->Set('login', 'default_domain', 'cpanel'); + $oConfig->Set('logs', 'path', $_ENV['HOME'] . '/logs/snappymail'); + $oConfig->Set('cache', 'path', $_ENV['HOME'] . '/tmp/snappymail'); $oConfig->Save(); $sFile = APP_PRIVATE_DATA.'domains/cpanel.json'; diff --git a/snappymail/v/0.0.0/include.php b/snappymail/v/0.0.0/include.php index d655c23e7..f1577d535 100644 --- a/snappymail/v/0.0.0/include.php +++ b/snappymail/v/0.0.0/include.php @@ -55,8 +55,7 @@ date_default_timezone_set('UTC'); $sCustomDataPath = ''; $sCustomConfiguration = ''; -if (is_file(APP_INDEX_ROOT_PATH.'include.php')) -{ +if (is_file(APP_INDEX_ROOT_PATH.'include.php')) { include_once APP_INDEX_ROOT_PATH.'include.php'; } @@ -72,8 +71,9 @@ unset($sPrivateDataFolderInternalName); if (!defined('APP_DATA_FOLDER_PATH')) { // cPanel https://github.com/the-djmaze/snappymail/issues/697 - if (!empty($_ENV['CPANEL']) && isset($_ENV['TMPDIR'])) { - $sCustomDataPath = $_ENV['TMPDIR'] . '/snappymail'; + if (!empty($_ENV['CPANEL']) && isset($_ENV['HOME'])) { + $sCustomDataPath = $_ENV['HOME'] . '/var/snappymail'; +// $sCustomDataPath = $_ENV['TMPDIR'] . '/snappymail'; } else { $sCustomDataPath = function_exists('__get_custom_data_full_path') ? rtrim(trim(__get_custom_data_full_path()), '\\/') : $sCustomDataPath; } From 5c02806a76eab60d97bcc129947d0921cb8ae9a6 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 25 Nov 2022 09:55:59 +0100 Subject: [PATCH 113/114] Handle the new logs/cache paths better --- .../v/0.0.0/app/libraries/RainLoop/Actions.php | 14 +++++++------- snappymail/v/0.0.0/setup.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index d9913e934..9e2c6c203 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -133,8 +133,9 @@ class Actions if ('syslog' === $sLogFileName) { $oDriver = new \MailSo\Log\Drivers\Syslog(); } else { - $sLogFileFullPath = \APP_PRIVATE_DATA . 'logs/' . $this->compileLogFileName($sLogFileName); - $oDriver = new \MailSo\Log\Drivers\File($sLogFileFullPath); + $sLogFileFullPath = \trim($this->oConfig->Get('logs', 'path', '')) ?: \APP_PRIVATE_DATA . 'logs'; + \is_dir($sLogFileFullPath) || \mkdir($sLogFileFullPath, 0700, true); + $oDriver = new \MailSo\Log\Drivers\File($sLogFileFullPath . '/' . $this->compileLogFileName($sLogFileName)); } $this->oLogger->append($oDriver ->SetTimeZone($this->oConfig->Get('logs', 'time_zone', 'UTC')) @@ -509,6 +510,8 @@ class Actions switch (true) { default: case $bForceFile: + $sCacheDir = \trim($this->oConfig->Get('cache', 'path', '')) ?: APP_PRIVATE_DATA . 'cache'; + \is_dir($sCacheDir) || \mkdir($sCacheDir, 0700, true); $oDriver = new \MailSo\Cache\Drivers\File( \trim($this->oConfig->Get('cache', 'path', APP_PRIVATE_DATA . 'cache')), $sKey @@ -569,13 +572,10 @@ class Actions if ($this->oConfig->Get('logs', 'auth_logging', false)) { // $this->oLoggerAuth->SetLevel(\LOG_WARNING); - $sAuthLogFileFullPath = \trim($this->oConfig->Get('logs', 'path', \APP_PRIVATE_DATA . 'logs')) + $sAuthLogFileFullPath = (\trim($this->oConfig->Get('logs', 'path', '') ?: \APP_PRIVATE_DATA . 'logs')) . '/' . $this->compileLogFileName($this->oConfig->Get('logs', 'auth_logging_filename', '')); $sLogFileDir = \dirname($sAuthLogFileFullPath); - if (!\is_dir($sLogFileDir)) { - \mkdir($sLogFileDir, 0755, true); - } - + \is_dir($sLogFileDir) || \mkdir($sLogFileDir, 0755, true); $this->oLoggerAuth->append( (new \MailSo\Log\Drivers\File($sAuthLogFileFullPath)) ->DisableTimePrefix() diff --git a/snappymail/v/0.0.0/setup.php b/snappymail/v/0.0.0/setup.php index 068fdfa2f..c74f4ee9b 100644 --- a/snappymail/v/0.0.0/setup.php +++ b/snappymail/v/0.0.0/setup.php @@ -135,7 +135,7 @@ if (defined('APP_VERSION')) clearstatcache(); } - foreach (array('logs', 'cache', 'configs', 'domains', 'plugins', 'storage') as $sName) + foreach (array('configs', 'domains', 'plugins', 'storage') as $sName) { if (!is_dir(APP_PRIVATE_DATA.$sName)) { From ce7bcbcaf532a3ea9356ccf4fb41ba5e2e3c2f28 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 25 Nov 2022 09:56:26 +0100 Subject: [PATCH 114/114] Added build script for cPanel --- build/cpanel.php | 39 +++++++++++++++++++++++++++++++++++++++ release.php | 6 +++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100755 build/cpanel.php diff --git a/build/cpanel.php b/build/cpanel.php new file mode 100755 index 000000000..2718b75aa --- /dev/null +++ b/build/cpanel.php @@ -0,0 +1,39 @@ +version}-cpanel.tar"; + +@unlink($cpanel_destination); +@unlink("{$cpanel_destination}.gz"); + +$cpanel_tar = new PharData($cpanel_destination); + +$cpanel_tar->buildFromDirectory('./integrations/cpanel', "@integrations/cpanel/@"); + +$cpanel_path = '/usr/local/cpanel/base/3rdparty/snappymail/'; + +$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST); +foreach ($files as $file) { + if (is_file($file)) { + $newFile = str_replace('\\', '/', $file); + $cpanel_tar->addFile($file, "{$cpanel_path}{$newFile}"); + } +} + +$cpanel_tar->addFile('.htaccess', "{$cpanel_path}.htaccess"); +//$cpanel_tar->addFile('_include.php', 'snappymail/app/_include.php'); +$cpanel_tar->addFile('README.md', "{$cpanel_path}README.md"); +$cpanel_tar->addFromString('VERSION', $package->version); + +$index = file_get_contents('index.php'); +$index = str_replace('0.0.0', $package->version, $index); +$cpanel_tar->addFromString("{$cpanel_path}index.php", $index); + +$data = file_get_contents('dev/serviceworker.js'); +$cpanel_tar->addFromString("{$cpanel_path}serviceworker.js", $data); + +$cpanel_tar->compress(Phar::GZ); +unlink($cpanel_destination); +$cpanel_destination .= '.gz'; + +echo "{$cpanel_destination} created\n"; diff --git a/release.php b/release.php index a784a62ea..51303e743 100755 --- a/release.php +++ b/release.php @@ -2,7 +2,7 @@ version}", 'snappymail/v/0.0.0'); file_put_contents("{$destPath}core.json", '{