From 5d8b067ca07729e4b55dcd932751c4d9974ca8da Mon Sep 17 00:00:00 2001 From: MI-KY <116441962+MI-KY@users.noreply.github.com> Date: Fri, 4 Nov 2022 22:04:52 +0100 Subject: [PATCH 0001/1012] corrected links to anchors --- integrations/nextcloud/snappymail/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/nextcloud/snappymail/README.md b/integrations/nextcloud/snappymail/README.md index 457aa08ab..5ab661d4e 100644 --- a/integrations/nextcloud/snappymail/README.md +++ b/integrations/nextcloud/snappymail/README.md @@ -26,7 +26,7 @@ After a quick wait, SnappyMail is installed. Now you should configure it before To enter SnappyMail admin area, you must be Nextcloud admin (so you get logged in automatically) or else use the admin login credentials. The default login is "admin" and the default password will be generated in `[nextcloud-data]/app_snappymail/_data_/_default_/admin_password.txt`. Don't forget to change it once in the admin panel! -From that point, all instance-wide SnappyMail settings can be tweaked as you wish. One important point is the "Domains" section where you should set up the IMAP/SMTP parameters that will be associated with the email adresses of your users. Basically, if a user of the Nextcloud instance starts SnappyMail and puts "firstname@domain.tld" as an email address, then SnappyMail should know how to connect to the IMAP & SMTP of domain.tld. You can fill in this information in the "Domains" section of the SnappyMail admin settings. For more information how to configure an automatic login for your Nextcloud users see [How to auto-connect to SnappyMail?](https://github.com/the-djmaze/snappymail/edit/master/integrations/nextcloud/snappymail/README.md#how-to-auto-connect-to-snappymail) +From that point, all instance-wide SnappyMail settings can be tweaked as you wish. One important point is the "Domains" section where you should set up the IMAP/SMTP parameters that will be associated with the email adresses of your users. Basically, if a user of the Nextcloud instance starts SnappyMail and puts "firstname@domain.tld" as an email address, then SnappyMail should know how to connect to the IMAP & SMTP of domain.tld. You can fill in this information in the "Domains" section of the SnappyMail admin settings. For more information how to configure an automatic login for your Nextcloud users see [How to auto-connect to SnappyMail?](#how-to-auto-connect-to-snappymail) ![grafik](https://user-images.githubusercontent.com/63400209/199767908-fbef0f50-ecb7-47ae-9ac1-771959d4b7f5.png) @@ -68,7 +68,7 @@ If no personal credentials are defined the user is prompted by SnappyMail to ins #### Option 2: Attempt to automatically login users with their Nextcloud username and password, or user-defined credentials, if set. If the user sets his credentials for the mailbox in his personal account under "Settings -> Additional settings", these credentials are used by SnappyMail to login. -If no personal credentials are defined the Nextcloud username and password is used by SnappyMail to login (eventually adding the [default domain](https://github.com/the-djmaze/snappymail/edit/master/integrations/nextcloud/snappymail/README.md#default-domain)). +If no personal credentials are defined the Nextcloud username and password is used by SnappyMail to login (eventually adding the [default domain](#default-domain)). If your IMAP server only accepts usernames without a domain (for example the ldap username of your user) the automatic addition of the "default domain" would block your users from logging in to your IMAP server - but on the other side it is needed by SnappyMail to determine the server settings to use. In such a case you must configure SnappyMail to strip off the domain part before sending the credentials to your IMAP server. This is done by entering to the SnappyMail Admin Panel -> Domains -> clicking on your default domain -> flagging the checkbox "Use short login" under IMAP and SMTP. From 8a0cb947f59bf762c593516b80199e5ce0b124d1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sat, 5 Nov 2022 02:27:55 +0100 Subject: [PATCH 0002/1012] Resolve #627 --- .../snappymail/lib/AppInfo/Application.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php index 5f0b883de..55b92449a 100644 --- a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -68,22 +68,9 @@ class Application extends App implements IBootstrap if (!\is_dir(\rtrim(\trim(\OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/') . '/appdata_snappymail')) { return; } -/* - $container = $this->getContainer(); - $container->query('OCP\INavigationManager')->add(function () use ($container) { - $urlGenerator = $container->query('OCP\IURLGenerator'); - return [ - 'id' => 'snappymail', - 'order' => 4, - 'href' => $urlGenerator->linkToRoute('snappymail.page.index'), - 'icon' => $urlGenerator->imagePath('snappymail', 'logo-white-64x64.png'), - 'name' => \OCP\Util::getL10N('snappymail')->t('Email') - ]; - }); -*/ - $dispatcher = $container->query('OCP\EventDispatcher\IEventDispatcher'); - $this->dispatcher->addListener(PostLoginEvent::class, function (PostLoginEvent $Event) { + $dispatcher = $context->getAppContainer()->query('OCP\EventDispatcher\IEventDispatcher'); + $dispatcher->addListener(PostLoginEvent::class, function (PostLoginEvent $Event) { $config = \OC::$server->getConfig(); // Only store the user's password in the current session if they have // enabled auto-login using Nextcloud username or email address. @@ -95,7 +82,7 @@ class Application extends App implements IBootstrap } }); - $this->dispatcher->addListener(BeforeUserLoggedOutEvent::class, function (BeforeUserLoggedOutEvent $Event) { + $dispatcher->addListener(BeforeUserLoggedOutEvent::class, function (BeforeUserLoggedOutEvent $Event) { \OC::$server->getSession()['snappymail-password'] = ''; SnappyMailHelper::loadApp(); \RainLoop\Api::Actions()->Logout(true); From 2693e944179f3ce41921500d487b501210f2df95 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sat, 5 Nov 2022 10:07:53 +0100 Subject: [PATCH 0003/1012] Resolve #624 --- dev/View/User/MailBox/MessageList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index fff561e13..c8009c018 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -644,7 +644,7 @@ export class MailMessageList extends AbstractViewRight { return false; }); - addShortcut('f,mailforward', 'shift', [Scope.MessageList, Scope.MessageView], () => { + registerShortcut('f,mailforward', 'shift', [Scope.MessageList, Scope.MessageView], () => { this.forwardCommand(); return false; }); From db426abbac9658bf5938cf0a436028f88dd6f77b Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sat, 5 Nov 2022 11:03:17 +0100 Subject: [PATCH 0004/1012] Some CSS borders to var(--border-color) --- dev/Styles/User/MessageList.less | 4 ++-- dev/Styles/User/MessageView.less | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dev/Styles/User/MessageList.less b/dev/Styles/User/MessageList.less index 6c59dcf48..5d38e3289 100644 --- a/dev/Styles/User/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -40,7 +40,7 @@ html.rl-no-preview-pane { border-bottom-right-radius: @rlMainBorderRadius; border-bottom-left-radius: @rlMainBorderRadius; - border-top: 1px solid #bbb; + border-top: 1px solid var(--border-color, #bbb); .e-quota { border-bottom: 1px dashed #333; @@ -74,7 +74,7 @@ html.rl-no-preview-pane { border-top-right-radius: @rlMainBorderRadius; border-top-left-radius: @rlMainBorderRadius; - border-bottom: 1px solid #bbb; + border-bottom: 1px solid var(--border-color, #bbb); } .checkboxCheckAll { diff --git a/dev/Styles/User/MessageView.less b/dev/Styles/User/MessageView.less index eb3eaa094..e10588103 100644 --- a/dev/Styles/User/MessageView.less +++ b/dev/Styles/User/MessageView.less @@ -116,7 +116,7 @@ html.rl-no-preview-pane { .messageItemHeader { background-color: #f8f8f8; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--border-color, #bbb); border-radius: @rlMainBorderRadius @rlMainBorderRadius 0 0; padding: 10px; flex-shrink: 0; @@ -290,14 +290,14 @@ html.rl-no-preview-pane { .bodySubHeader > * { background-color: #eee; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--border-color, #bbb); cursor: pointer; padding: 10px 15px; } .pgpInfo { padding: 5px 15px; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--border-color, #bbb); background-color: #fcf8e3; &.success { @@ -313,7 +313,7 @@ html.rl-no-preview-pane { padding: 10px 10px 6px 10px; background: #eee; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--border-color, #bbb); position: relative; .attachmentList { @@ -343,7 +343,7 @@ html.rl-no-preview-pane { .attachmentsControls { padding: 7px 5px 7px 14px; background: #e8e8e8; - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--border-color, #bbb); } .attachmentsControls > * { margin-right: 0.5em; From 0feae48464d4724e58223d551ee7430426a07209 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sat, 5 Nov 2022 12:04:56 +0100 Subject: [PATCH 0005/1012] Bugfix: pgpDecrypt() using MailVelope the decrypt message was not green --- dev/View/User/MailBox/MessageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index df0f93263..8d1bd6bd3 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -530,8 +530,8 @@ export class MailMessageView extends AbstractViewRight { const oMessage = currentMessage(); PgpUserStore.decrypt(oMessage).then(result => { if (result) { + oMessage.pgpDecrypted(true); if (result.data) { - oMessage.pgpDecrypted(true); MimeToMessage(result.data, oMessage); oMessage.html() ? oMessage.viewHtml() : oMessage.viewPlain(); if (result.signatures?.length) { From 115437fb6a260ab6b75906c0d923b2ceb3972edc Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sat, 5 Nov 2022 12:07:26 +0100 Subject: [PATCH 0006/1012] v2.20.2 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 19 +++++++++++++++ README.md | 24 +++++++++---------- build/nextcloud.php | 4 ++-- dev/Knoin/AbstractViews.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 +- 10 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index 4655a1599..03c12d1ff 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -290,5 +290,5 @@ dev_email = "" dev_password = "" [version] -current = "2.20.0" +current = "2.20.2" saved = "Fri, 30 Sep 2022 12:34:56 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index 642c3421f..bb9e1fa1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## 2.20.2 – 2022-11-05 + +### Added +- Add more search operators (i.e. copy lots of Gmail ones) + [#625](https://github.com/the-djmaze/snappymail/issues/625) + +### Changed +- Some CSS borders to var(--border-color) + +### Fixed +- pgpDecrypt() using MailVelope the decrypt message was not green +- Shift + F in search bar resulted in forwarding message + [#624](https://github.com/the-djmaze/snappymail/issues/624) + +### Nextcloud +- auto login mechanism not working anymore + [#627](https://github.com/the-djmaze/snappymail/issues/627) + + ## 2.20.1 – 2022-11-04 ### Added diff --git a/README.md b/README.md index 24c15f92a..51643e8d6 100644 --- a/README.md +++ b/README.md @@ -140,26 +140,26 @@ RainLoop 1.17 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.170.153 | 81.545 | -|app.js |4.207.787 | 406.032 | +|admin.js |2.170.153 | 81.587 | +|app.js |4.207.787 | 406.110 | |boot.js | 868.735 | 2.050 | |libs.js | 658.812 | 192.965 | |sieve.js | 0 | 86.121 | |polyfills.js | 334.608 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.240.095 | 768.998 | +|TOTAL |8.240.095 | 769.118 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 256.831 | 40.255 | 73.606 | 13.435 | 60.877 | 12.047 | -|app.min.js | 515.367 | 188.046 |139.456 | 62.537 |110.485 | 53.693 | +|admin.min.js | 256.831 | 40.266 | 73.606 | 13.442 | 60.877 | 12.047 | +|app.min.js | 515.367 | 188.117 |139.456 | 62.542 |110.485 | 53.594 | |boot.min.js | 84.659 | 1.252 | 26.998 | 778 | 23.643 | 628 | |libs.min.js | 584.772 | 92.055 |180.901 | 34.113 |155.182 | 30.641 | |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.353 |358.761 | 97.428 |299.485 | 84.962 | -|TOTAL user+sieve |1.217.635 | 323.279 |358.761 |107.912 |299.485 | 94.413 | -|TOTAL admin | 959.099 | 133.562 |292.911 | 48.326 |249.877 | 43.316 | +|TOTAL user |1.217.635 | 281.424 |358.761 | 97.433 |299.485 | 84.863 | +|TOTAL user+sieve |1.217.635 | 323.350 |358.761 |107.917 |299.485 | 94.314 | +|TOTAL admin | 959.099 | 133.573 |292.911 | 48.333 |249.877 | 43.316 | For a user its around 70% smaller and faster than traditional RainLoop. @@ -187,12 +187,12 @@ 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 | 82.870 | 46.946 | 17.215 | 14.727 | -|app.min.css | 274.947 | 66.646 | 39.647 | 15.260 | 13.322 | +|app.css | 340.331 | 83.119 | 46.946 | 17.231 | 14.754 | +|app.min.css | 274.947 | 66.879 | 39.647 | 15.274 | 13.333 | |boot.css | | 1.326 | | 664 | 545 | |boot.min.css | | 1.071 | | 590 | 474 | -|admin.css | | 30.506 | | 6.907 | 5.998 | -|admin.min.css | | 24.677 | | 6.267 | 5.498 | +|admin.css | | 30.603 | | 6.925 | 6.004 | +|admin.min.css | | 24.764 | | 6.286 | 5.512 | ### PGP RainLoop uses the old OpenPGP.js v2 diff --git a/build/nextcloud.php b/build/nextcloud.php index a9bdf7907..e3708995c 100755 --- a/build/nextcloud.php +++ b/build/nextcloud.php @@ -14,7 +14,7 @@ $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymai foreach ($files as $file) { if (is_file($file)) { $newFile = str_replace('\\', '/', $file); - $newFile = str_replace("'snappymail/v/'.", '', $newFile); +// $newFile = str_replace("'snappymail/v/'.", '', $newFile); $nc_tar->addFile($file, "snappymail/app/{$newFile}"); } } @@ -28,7 +28,7 @@ $nc_tar->addFile('.htaccess', 'snappymail/app/.htaccess'); $index = file_get_contents('index.php'); $index = str_replace('0.0.0', $package->version, $index); -$index = str_replace('snappymail/v/', '', $index); +//$index = str_replace('snappymail/v/', '', $index); $nc_tar->addFromString('snappymail/app/index.php', $index); $nc_tar->addFile('README.md', 'snappymail/app/README.md'); $nc_tar->addFile('CHANGELOG.md', 'snappymail/CHANGELOG.md'); diff --git a/dev/Knoin/AbstractViews.js b/dev/Knoin/AbstractViews.js index 9a7abac5d..91be6625e 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, SettingsGet, leftPanelDisabled, elementById } from 'Common/Globals'; +import { keyScope, addShortcut, SettingsGet, leftPanelDisabled, elementById } from 'Common/Globals'; import { ViewTypePopup, showScreenPopup } from 'Knoin/Knoin'; import { SaveSettingStatus } from 'Common/Enums'; @@ -59,7 +59,7 @@ export class AbstractViewPopup extends AbstractView super('Popups' + name, ViewTypePopup); this.keyScope.scope = name; this.modalVisible = ko.observable(false).extend({ rateLimit: 0 }); - shortcuts.add('escape,close', '', name, () => { + addShortcut('escape,close', '', name, () => { if (this.modalVisible() && false !== this.onClose()) { this.close(); } diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 527cd0162..f5befb43d 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.20.0. +This app packages SnappyMail 2.20.2. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index fc6783dd8..cb352237a 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.20.0 +VERSION=2.20.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 69edd9059..1d8c1703e 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.20.1 + 2.20.2 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli Date: Sun, 6 Nov 2022 14:39:19 +0100 Subject: [PATCH 0007/1012] Prevent notices in MailSo\Net\ConnectSettings --- .../v/0.0.0/app/libraries/MailSo/Net/ConnectSettings.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 f1c0ad52e..8bb34edbd 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 @@ -68,9 +68,9 @@ class ConnectSettings $object->host = $aSettings['Host']; $object->port = $aSettings['Port']; $object->type = $aSettings['Secure']; - $object->ssl['verify_peer'] = !!$aSettings['VerifySsl']; - $object->ssl['verify_peer_name'] = !!$aSettings['VerifySsl']; - $object->ssl['allow_self_signed'] = !!$aSettings['AllowSelfSigned']; + $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']; } From 84e0fa5756498e741281be68472f3cd4116c2a99 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sun, 6 Nov 2022 14:45:12 +0100 Subject: [PATCH 0008/1012] Resolve #634 --- .../nextcloud/snappymail/lib/Controller/PageController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/nextcloud/snappymail/lib/Controller/PageController.php b/integrations/nextcloud/snappymail/lib/Controller/PageController.php index 850117465..188d9330e 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/PageController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/PageController.php @@ -84,7 +84,7 @@ class PageController extends Controller $csp = new ContentSecurityPolicy(); $csp->addAllowedScriptDomain("'self'"); - $csp->useStrictDynamic(true); + \method_exists($csp, 'useStrictDynamic') && $csp->useStrictDynamic(true); // NC24+ $csp->allowEvalScript(true); // $csp->addAllowedScriptDomain("'unsafe-eval'"); $csp->addAllowedStyleDomain("'self'"); $response->setContentSecurityPolicy($csp); From 6e4b6e543f0fd539c21aec3a029b311b44bb1200 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sun, 6 Nov 2022 14:46:54 +0100 Subject: [PATCH 0009/1012] Throw, catch then log decrypt errors for #632 --- .../0.0.0/app/libraries/snappymail/crypt.php | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php b/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php index 6cecb4d8b..bb2327474 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/crypt.php @@ -55,7 +55,9 @@ abstract class Crypt $fn = "{$data[0]}Decrypt"; if (\method_exists(__CLASS__, $fn)) { $result = static::{$fn}($data[2], $data[1], $key); - return \json_decode($result, true); + if (\is_string($result)) { + return static::jsonDecode($result); + } } } catch (\Throwable $e) { \trigger_error(__CLASS__ . "::{$fn}(): " . $e->getMessage()); @@ -68,7 +70,7 @@ abstract class Crypt public static function DecryptFromJSON(string $data, string $key = null) /* : mixed */ { - $data = \json_decode($data, true); + $data = static::jsonDecode($data); if (!\is_array($data)) { // \trigger_error(__CLASS__ . '::DecryptFromJSON() invalid $data'); return null; @@ -89,22 +91,26 @@ abstract class Crypt public static function Encrypt($data, string $key = null) : array { $data = \json_encode($data); - if (\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) { + + try { $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES); - $result = ['sodium', $nonce, static::SodiumEncrypt($data, $nonce, $key)]; - } else if (static::$cipher && \is_callable('openssl_encrypt')) { - $iv = \random_bytes(\openssl_cipher_iv_length(static::$cipher)); - $result = ['openssl', $iv, static::OpenSSLEncrypt($data, $iv, $key)]; - } else { - $salt = \random_bytes(16); - $result = ['xxtea', $salt, static::XxteaEncrypt($data, $salt, $key)]; + return ['sodium', $nonce, static::SodiumEncrypt($data, $nonce, $key)]; + } catch (\Throwable $e) { } + + try { + $iv = \random_bytes(\openssl_cipher_iv_length(static::$cipher)); + return ['openssl', $iv, static::OpenSSLEncrypt($data, $iv, $key)]; + } catch (\Throwable $e) { + } + + $salt = \random_bytes(16); + return ['xxtea', $salt, static::XxteaEncrypt($data, $salt, $key)]; /* if (static::{"{$result[0]}Decrypt"}($result[2], $result[1], $key) !== $data) { throw new \Exception('Encrypt/Decrypt mismatch'); } */ - return $result; } public static function EncryptToJSON($data, string $key = null) : string @@ -117,10 +123,10 @@ abstract class Crypt return \implode('.', \array_map('MailSo\\Base\\Utils::UrlSafeBase64Encode', static::Encrypt($data, $key))); } - public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : mixed */ + public static function SodiumDecrypt(string $data, string $nonce, string $key = null) /* : string|false */ { if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) { - return null; + throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt not callable'); } return \sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( $data, @@ -130,10 +136,10 @@ abstract class Crypt ); } - public static function SodiumEncrypt(string $data, string $nonce, string $key = null) : ?string + public static function SodiumEncrypt(string $data, string $nonce, string $key = null) : string { if (!\is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) { - return null; + throw new \Exception('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt not callable'); } return \sodium_crypto_aead_xchacha20poly1305_ietf_encrypt( $data, @@ -143,10 +149,13 @@ abstract class Crypt ); } - public static function OpenSSLDecrypt(string $data, string $iv, string $key = null) /* : mixed */ + public static function OpenSSLDecrypt(string $data, string $iv, string $key = null) /* : string|false */ { - if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_decrypt')) { - return null; + if (!$data || !$iv) { + throw new \InvalidArgumentException('$data or $iv is empty string'); + } + if (!static::$cipher || !\is_callable('openssl_decrypt')) { + throw new \Exception('openssl_decrypt not callable'); } return \openssl_decrypt( $data, @@ -159,8 +168,11 @@ abstract class Crypt public static function OpenSSLEncrypt(string $data, string $iv, string $key = null) : ?string { - if (!$data || !$iv || !static::$cipher || !\is_callable('openssl_encrypt')) { - return null; + if (!$data || !$iv) { + throw new \InvalidArgumentException('$data or $iv is empty string'); + } + if (!static::$cipher || !\is_callable('openssl_encrypt')) { + throw new \Exception('openssl_encrypt not callable'); } return \openssl_encrypt( $data, @@ -174,7 +186,7 @@ abstract class Crypt public static function XxteaDecrypt(string $data, string $salt, string $key = null) /* : mixed */ { if (!$data || !$salt) { - return null; + throw new \InvalidArgumentException('$data or $salt is empty string'); } $key = $salt . static::Passphrase($key); return \is_callable('xxtea_decrypt') @@ -185,7 +197,7 @@ abstract class Crypt public static function XxteaEncrypt(string $data, string $salt, string $key = null) : ?string { if (!$data || !$salt) { - return null; + throw new \InvalidArgumentException('$data or $salt is empty string'); } $key = $salt . static::Passphrase($key); return \is_callable('xxtea_encrypt') @@ -193,6 +205,11 @@ abstract class Crypt : \MailSo\Base\Xxtea::encrypt($data, $key); } + private static function jsonDecode(string $data) /*: mixed*/ + { + return \json_decode($data, true, 512, JSON_THROW_ON_ERROR); + } + } \SnappyMail\Crypt::setCipher(\RainLoop\Api::Config()->Get('security', 'encrypt_cipher', 'aes-256-cbc-hmac-sha1')) From e0320c63c103615662dbd8a0d2406dd96abfb1c2 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Sun, 6 Nov 2022 15:30:08 +0100 Subject: [PATCH 0010/1012] Remove CKeditor 4 because it never works with Content-Security-Policy See #573 and #630 --- plugins/ckeditor/ckeditor.js | 92 -- plugins/ckeditor/index.php | 41 - plugins/ckeditor/src/CHANGES.md | 1219 -------------- plugins/ckeditor/src/FIXES.txt | 12 - plugins/ckeditor/src/LICENSE.md | 1420 ----------------- plugins/ckeditor/src/README.md | 39 - plugins/ckeditor/src/build-config.js | 152 -- plugins/ckeditor/src/ckeditor.js | 1010 ------------ plugins/ckeditor/src/config.js | 34 - plugins/ckeditor/src/contents.css | 208 --- plugins/ckeditor/src/lang/af.js | 5 - plugins/ckeditor/src/lang/ar.js | 244 --- plugins/ckeditor/src/lang/az.js | 5 - plugins/ckeditor/src/lang/bg.js | 11 - plugins/ckeditor/src/lang/bn.js | 19 - plugins/ckeditor/src/lang/bs.js | 5 - plugins/ckeditor/src/lang/ca.js | 5 - plugins/ckeditor/src/lang/cs.js | 5 - plugins/ckeditor/src/lang/cy.js | 5 - plugins/ckeditor/src/lang/da.js | 5 - plugins/ckeditor/src/lang/de-ch.js | 5 - plugins/ckeditor/src/lang/de.js | 5 - plugins/ckeditor/src/lang/el.js | 146 -- plugins/ckeditor/src/lang/en-au.js | 5 - plugins/ckeditor/src/lang/en-ca.js | 5 - plugins/ckeditor/src/lang/en-gb.js | 5 - plugins/ckeditor/src/lang/en.js | 5 - plugins/ckeditor/src/lang/eo.js | 5 - plugins/ckeditor/src/lang/es.js | 5 - plugins/ckeditor/src/lang/et.js | 5 - plugins/ckeditor/src/lang/eu.js | 5 - plugins/ckeditor/src/lang/fa.js | 167 -- plugins/ckeditor/src/lang/fi.js | 5 - plugins/ckeditor/src/lang/fo.js | 5 - plugins/ckeditor/src/lang/fr-ca.js | 5 - plugins/ckeditor/src/lang/fr.js | 5 - plugins/ckeditor/src/lang/gl.js | 5 - plugins/ckeditor/src/lang/gu.js | 36 - plugins/ckeditor/src/lang/he.js | 5 - plugins/ckeditor/src/lang/hi.js | 42 - plugins/ckeditor/src/lang/hr.js | 5 - plugins/ckeditor/src/lang/hu.js | 5 - plugins/ckeditor/src/lang/id.js | 5 - plugins/ckeditor/src/lang/is.js | 5 - plugins/ckeditor/src/lang/it.js | 5 - plugins/ckeditor/src/lang/ja.js | 73 - plugins/ckeditor/src/lang/ka.js | 5 - plugins/ckeditor/src/lang/km.js | 124 -- plugins/ckeditor/src/lang/ko.js | 82 - plugins/ckeditor/src/lang/ku.js | 104 -- plugins/ckeditor/src/lang/lt.js | 36 - plugins/ckeditor/src/lang/lv.js | 5 - plugins/ckeditor/src/lang/mk.js | 5 - plugins/ckeditor/src/lang/mn.js | 166 -- plugins/ckeditor/src/lang/ms.js | 5 - plugins/ckeditor/src/lang/nb.js | 5 - plugins/ckeditor/src/lang/nl.js | 5 - plugins/ckeditor/src/lang/no.js | 5 - plugins/ckeditor/src/lang/oc.js | 5 - plugins/ckeditor/src/lang/pl.js | 66 - plugins/ckeditor/src/lang/pt-br.js | 5 - plugins/ckeditor/src/lang/pt.js | 5 - plugins/ckeditor/src/lang/ro.js | 5 - plugins/ckeditor/src/lang/ru.js | 22 - plugins/ckeditor/src/lang/si.js | 59 - plugins/ckeditor/src/lang/sk.js | 5 - plugins/ckeditor/src/lang/sl.js | 5 - plugins/ckeditor/src/lang/sq.js | 5 - plugins/ckeditor/src/lang/sr-latn.js | 5 - plugins/ckeditor/src/lang/sr.js | 9 - plugins/ckeditor/src/lang/sv.js | 8 - plugins/ckeditor/src/lang/th.js | 5 - plugins/ckeditor/src/lang/tr.js | 5 - plugins/ckeditor/src/lang/tt.js | 8 - plugins/ckeditor/src/lang/ug.js | 140 -- plugins/ckeditor/src/lang/uk.js | 18 - plugins/ckeditor/src/lang/vi.js | 5 - plugins/ckeditor/src/lang/zh-cn.js | 103 -- plugins/ckeditor/src/lang/zh.js | 74 - .../src/plugins/about/dialogs/about.js | 8 - .../about/dialogs/hidpi/logo_ckeditor.png | Bin 13339 -> 0 bytes .../plugins/about/dialogs/logo_ckeditor.png | Bin 6757 -> 0 bytes .../src/plugins/base64image/LICENSE.md | 1243 --------------- .../src/plugins/base64image/README.md | 21 - .../base64image/dialogs/base64image.js | 16 - .../src/plugins/clipboard/dialogs/paste.js | 12 - .../colordialog/dialogs/colordialog.css | 20 - .../colordialog/dialogs/colordialog.js | 14 - .../src/plugins/dialog/dialogDefinition.js | 4 - plugins/ckeditor/src/plugins/icons.png | Bin 5775 -> 0 bytes plugins/ckeditor/src/plugins/icons_hidpi.png | Bin 17424 -> 0 bytes .../src/plugins/image/dialogs/image.js | 44 - .../src/plugins/image/images/noimage.png | Bin 1610 -> 0 bytes .../src/plugins/image2/dialogs/image2.js | 15 - .../src/plugins/link/dialogs/anchor.js | 7 - .../ckeditor/src/plugins/link/dialogs/link.js | 28 - .../src/plugins/link/images/anchor.png | Bin 752 -> 0 bytes .../src/plugins/link/images/hidpi/anchor.png | Bin 1109 -> 0 bytes .../plugins/plain/icons/hidpi/plain-rtl.png | Bin 1968 -> 0 bytes .../src/plugins/plain/icons/hidpi/plain.png | Bin 1999 -> 0 bytes .../src/plugins/plain/icons/plain-rtl.png | Bin 762 -> 0 bytes .../src/plugins/plain/icons/plain.png | Bin 764 -> 0 bytes .../plain/plain/icons/hidpi/plain-rtl.png | Bin 1968 -> 0 bytes .../plugins/plain/plain/icons/hidpi/plain.png | Bin 1999 -> 0 bytes .../plugins/plain/plain/icons/plain-rtl.png | Bin 762 -> 0 bytes .../src/plugins/plain/plain/icons/plain.png | Bin 764 -> 0 bytes .../src/plugins/plain/plain/plugin.js | 170 -- plugins/ckeditor/src/plugins/plain/plugin.js | 170 -- .../src/plugins/quicktable/CHANGELOG.md | 38 - .../src/plugins/quicktable/Gruntfile.js | 5 - .../ckeditor/src/plugins/quicktable/LICENSE | 339 ---- .../ckeditor/src/plugins/quicktable/README.md | 52 - .../src/plugins/quicktable/package.json | 31 - .../src/plugins/quicktable/template.jst | 469 ------ .../ckeditor/src/plugins/signature/plugin.js | 141 -- .../src/plugins/signature/signature/plugin.js | 141 -- .../src/plugins/table/dialogs/table.js | 21 - .../src/plugins/widget/images/handle.png | Bin 220 -> 0 bytes .../ckeditor/src/skins/moono-lisa/dialog.css | 5 - .../src/skins/moono-lisa/dialog_ie.css | 5 - .../src/skins/moono-lisa/dialog_ie8.css | 5 - .../src/skins/moono-lisa/dialog_iequirks.css | 5 - .../ckeditor/src/skins/moono-lisa/editor.css | 5 - .../src/skins/moono-lisa/editor_gecko.css | 5 - .../src/skins/moono-lisa/editor_ie.css | 5 - .../src/skins/moono-lisa/editor_ie8.css | 5 - .../src/skins/moono-lisa/editor_iequirks.css | 5 - .../ckeditor/src/skins/moono-lisa/icons.png | Bin 5775 -> 0 bytes .../src/skins/moono-lisa/icons_hidpi.png | Bin 17424 -> 0 bytes .../src/skins/moono-lisa/images/arrow.png | Bin 191 -> 0 bytes .../src/skins/moono-lisa/images/close.png | Bin 615 -> 0 bytes .../skins/moono-lisa/images/hidpi/close.png | Bin 1238 -> 0 bytes .../moono-lisa/images/hidpi/lock-open.png | Bin 1071 -> 0 bytes .../skins/moono-lisa/images/hidpi/lock.png | Bin 1062 -> 0 bytes .../skins/moono-lisa/images/hidpi/refresh.png | Bin 1623 -> 0 bytes .../src/skins/moono-lisa/images/lock-open.png | Bin 511 -> 0 bytes .../src/skins/moono-lisa/images/lock.png | Bin 506 -> 0 bytes .../src/skins/moono-lisa/images/refresh.png | Bin 757 -> 0 bytes .../src/skins/moono-lisa/images/spinner.gif | Bin 2984 -> 0 bytes .../ckeditor/src/skins/moono-lisa/readme.md | 46 - plugins/ckeditor/src/styles.js | 137 -- plugins/ckeditor/style.css | 207 --- 142 files changed, 9658 deletions(-) delete mode 100644 plugins/ckeditor/ckeditor.js delete mode 100644 plugins/ckeditor/index.php delete mode 100644 plugins/ckeditor/src/CHANGES.md delete mode 100644 plugins/ckeditor/src/FIXES.txt delete mode 100644 plugins/ckeditor/src/LICENSE.md delete mode 100644 plugins/ckeditor/src/README.md delete mode 100644 plugins/ckeditor/src/build-config.js delete mode 100644 plugins/ckeditor/src/ckeditor.js delete mode 100644 plugins/ckeditor/src/config.js delete mode 100644 plugins/ckeditor/src/contents.css delete mode 100644 plugins/ckeditor/src/lang/af.js delete mode 100644 plugins/ckeditor/src/lang/ar.js delete mode 100644 plugins/ckeditor/src/lang/az.js delete mode 100644 plugins/ckeditor/src/lang/bg.js delete mode 100644 plugins/ckeditor/src/lang/bn.js delete mode 100644 plugins/ckeditor/src/lang/bs.js delete mode 100644 plugins/ckeditor/src/lang/ca.js delete mode 100644 plugins/ckeditor/src/lang/cs.js delete mode 100644 plugins/ckeditor/src/lang/cy.js delete mode 100644 plugins/ckeditor/src/lang/da.js delete mode 100644 plugins/ckeditor/src/lang/de-ch.js delete mode 100644 plugins/ckeditor/src/lang/de.js delete mode 100644 plugins/ckeditor/src/lang/el.js delete mode 100644 plugins/ckeditor/src/lang/en-au.js delete mode 100644 plugins/ckeditor/src/lang/en-ca.js delete mode 100644 plugins/ckeditor/src/lang/en-gb.js delete mode 100644 plugins/ckeditor/src/lang/en.js delete mode 100644 plugins/ckeditor/src/lang/eo.js delete mode 100644 plugins/ckeditor/src/lang/es.js delete mode 100644 plugins/ckeditor/src/lang/et.js delete mode 100644 plugins/ckeditor/src/lang/eu.js delete mode 100644 plugins/ckeditor/src/lang/fa.js delete mode 100644 plugins/ckeditor/src/lang/fi.js delete mode 100644 plugins/ckeditor/src/lang/fo.js delete mode 100644 plugins/ckeditor/src/lang/fr-ca.js delete mode 100644 plugins/ckeditor/src/lang/fr.js delete mode 100644 plugins/ckeditor/src/lang/gl.js delete mode 100644 plugins/ckeditor/src/lang/gu.js delete mode 100644 plugins/ckeditor/src/lang/he.js delete mode 100644 plugins/ckeditor/src/lang/hi.js delete mode 100644 plugins/ckeditor/src/lang/hr.js delete mode 100644 plugins/ckeditor/src/lang/hu.js delete mode 100644 plugins/ckeditor/src/lang/id.js delete mode 100644 plugins/ckeditor/src/lang/is.js delete mode 100644 plugins/ckeditor/src/lang/it.js delete mode 100644 plugins/ckeditor/src/lang/ja.js delete mode 100644 plugins/ckeditor/src/lang/ka.js delete mode 100644 plugins/ckeditor/src/lang/km.js delete mode 100644 plugins/ckeditor/src/lang/ko.js delete mode 100644 plugins/ckeditor/src/lang/ku.js delete mode 100644 plugins/ckeditor/src/lang/lt.js delete mode 100644 plugins/ckeditor/src/lang/lv.js delete mode 100644 plugins/ckeditor/src/lang/mk.js delete mode 100644 plugins/ckeditor/src/lang/mn.js delete mode 100644 plugins/ckeditor/src/lang/ms.js delete mode 100644 plugins/ckeditor/src/lang/nb.js delete mode 100644 plugins/ckeditor/src/lang/nl.js delete mode 100644 plugins/ckeditor/src/lang/no.js delete mode 100644 plugins/ckeditor/src/lang/oc.js delete mode 100644 plugins/ckeditor/src/lang/pl.js delete mode 100644 plugins/ckeditor/src/lang/pt-br.js delete mode 100644 plugins/ckeditor/src/lang/pt.js delete mode 100644 plugins/ckeditor/src/lang/ro.js delete mode 100644 plugins/ckeditor/src/lang/ru.js delete mode 100644 plugins/ckeditor/src/lang/si.js delete mode 100644 plugins/ckeditor/src/lang/sk.js delete mode 100644 plugins/ckeditor/src/lang/sl.js delete mode 100644 plugins/ckeditor/src/lang/sq.js delete mode 100644 plugins/ckeditor/src/lang/sr-latn.js delete mode 100644 plugins/ckeditor/src/lang/sr.js delete mode 100644 plugins/ckeditor/src/lang/sv.js delete mode 100644 plugins/ckeditor/src/lang/th.js delete mode 100644 plugins/ckeditor/src/lang/tr.js delete mode 100644 plugins/ckeditor/src/lang/tt.js delete mode 100644 plugins/ckeditor/src/lang/ug.js delete mode 100644 plugins/ckeditor/src/lang/uk.js delete mode 100644 plugins/ckeditor/src/lang/vi.js delete mode 100644 plugins/ckeditor/src/lang/zh-cn.js delete mode 100644 plugins/ckeditor/src/lang/zh.js delete mode 100644 plugins/ckeditor/src/plugins/about/dialogs/about.js delete mode 100644 plugins/ckeditor/src/plugins/about/dialogs/hidpi/logo_ckeditor.png delete mode 100644 plugins/ckeditor/src/plugins/about/dialogs/logo_ckeditor.png delete mode 100644 plugins/ckeditor/src/plugins/base64image/LICENSE.md delete mode 100644 plugins/ckeditor/src/plugins/base64image/README.md delete mode 100644 plugins/ckeditor/src/plugins/base64image/dialogs/base64image.js delete mode 100644 plugins/ckeditor/src/plugins/clipboard/dialogs/paste.js delete mode 100644 plugins/ckeditor/src/plugins/colordialog/dialogs/colordialog.css delete mode 100644 plugins/ckeditor/src/plugins/colordialog/dialogs/colordialog.js delete mode 100644 plugins/ckeditor/src/plugins/dialog/dialogDefinition.js delete mode 100644 plugins/ckeditor/src/plugins/icons.png delete mode 100644 plugins/ckeditor/src/plugins/icons_hidpi.png delete mode 100644 plugins/ckeditor/src/plugins/image/dialogs/image.js delete mode 100644 plugins/ckeditor/src/plugins/image/images/noimage.png delete mode 100644 plugins/ckeditor/src/plugins/image2/dialogs/image2.js delete mode 100644 plugins/ckeditor/src/plugins/link/dialogs/anchor.js delete mode 100644 plugins/ckeditor/src/plugins/link/dialogs/link.js delete mode 100644 plugins/ckeditor/src/plugins/link/images/anchor.png delete mode 100644 plugins/ckeditor/src/plugins/link/images/hidpi/anchor.png delete mode 100644 plugins/ckeditor/src/plugins/plain/icons/hidpi/plain-rtl.png delete mode 100644 plugins/ckeditor/src/plugins/plain/icons/hidpi/plain.png delete mode 100644 plugins/ckeditor/src/plugins/plain/icons/plain-rtl.png delete mode 100644 plugins/ckeditor/src/plugins/plain/icons/plain.png delete mode 100644 plugins/ckeditor/src/plugins/plain/plain/icons/hidpi/plain-rtl.png delete mode 100644 plugins/ckeditor/src/plugins/plain/plain/icons/hidpi/plain.png delete mode 100644 plugins/ckeditor/src/plugins/plain/plain/icons/plain-rtl.png delete mode 100644 plugins/ckeditor/src/plugins/plain/plain/icons/plain.png delete mode 100644 plugins/ckeditor/src/plugins/plain/plain/plugin.js delete mode 100644 plugins/ckeditor/src/plugins/plain/plugin.js delete mode 100644 plugins/ckeditor/src/plugins/quicktable/CHANGELOG.md delete mode 100644 plugins/ckeditor/src/plugins/quicktable/Gruntfile.js delete mode 100644 plugins/ckeditor/src/plugins/quicktable/LICENSE delete mode 100644 plugins/ckeditor/src/plugins/quicktable/README.md delete mode 100644 plugins/ckeditor/src/plugins/quicktable/package.json delete mode 100644 plugins/ckeditor/src/plugins/quicktable/template.jst delete mode 100644 plugins/ckeditor/src/plugins/signature/plugin.js delete mode 100644 plugins/ckeditor/src/plugins/signature/signature/plugin.js delete mode 100644 plugins/ckeditor/src/plugins/table/dialogs/table.js delete mode 100644 plugins/ckeditor/src/plugins/widget/images/handle.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/dialog.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/dialog_ie.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/dialog_ie8.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/dialog_iequirks.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/editor.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/editor_gecko.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/editor_ie.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/editor_ie8.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/editor_iequirks.css delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/icons.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/icons_hidpi.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/arrow.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/close.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/hidpi/close.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/hidpi/lock-open.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/hidpi/lock.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/hidpi/refresh.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/lock-open.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/lock.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/refresh.png delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/images/spinner.gif delete mode 100644 plugins/ckeditor/src/skins/moono-lisa/readme.md delete mode 100644 plugins/ckeditor/src/styles.js delete mode 100644 plugins/ckeditor/style.css diff --git a/plugins/ckeditor/ckeditor.js b/plugins/ckeditor/ckeditor.js deleted file mode 100644 index c5a8286ce..000000000 --- a/plugins/ckeditor/ckeditor.js +++ /dev/null @@ -1,92 +0,0 @@ -(rl => { - if (rl) { - const path = (rl.settings.app('webVersionPath') || 'snappymail/v/' + rl.settings.app('version') + '/'), - script = document.createElement('script'); - script.src = path + 'static/ckeditor/ckeditor.js'; - document.head.append(script); - - rl.createWYSIWYG = (container, onReady) => { - if (!window.CKEDITOR) { - return null; - } - - const - language = (rl.settings.get('Language') || 'en').toLowerCase(), - htmlEditorLangsMap = { - 'ar_sa': 'ar-sa', - 'pt_br': 'pt-br', - 'zh_cn': 'zh-cn', - }, - editor = CKEDITOR.appendTo(container, { - title: false, - stylesSet: false, -// customConfig: '', -// contentsCss: '', - toolbarGroups: [ - { name: 'spec' }, - { name: 'styles' }, - { name: 'basicstyles', groups: ['basicstyles', 'cleanup', 'bidi'] }, - { name: 'colors' }, - { name: 'paragraph', groups: ['list', 'indent', 'blocks', 'align'] }, - { name: 'links' }, - { name: 'insert' }, - { name: 'document', groups: ['mode', 'document', 'doctools'] }, - { name: 'others' } - ], - - removePlugins: 'liststyle' + (rl.settings.app('allowHtmlEditorBitiButtons') ? '' : ',bidi'), - removeButtons: 'Format,Undo,Redo,Cut,Copy,Paste,Anchor,Strike,Subscript,Superscript,Image,SelectAll', - removeDialogTabs: 'link:advanced;link:target;image:advanced;images:advanced', - - extraPlugins: 'plain,signature', - - allowedContent: true, - extraAllowedContent: true, - - fillEmptyBlocks: false, -// ignoreEmptyParagraph: true, - disableNativeSpellChecker: false, - - colorButton_enableAutomatic: false, -// colorButton_enableMore: true, - - font_defaultLabel: 'Arial', - fontSize_defaultLabel: '13', - fontSize_sizes: '10/10px;12/12px;13/13px;14/14px;16/16px;18/18px;20/20px;24/24px;28/28px;36/36px;48/48px', - - enterMode: CKEDITOR.ENTER_BR, - shiftEnterMode: CKEDITOR.ENTER_P, - language: htmlEditorLangsMap[language] || language.substr(0,2) - }); - - editor.on('key', event => !(event && event.data && 'Tab' == event.data.key)); - - editor.on('drop', event => { - const dt = event.data.dataTransfer, - id = dt.id, - file = dt.getFilesCount() ? dt.getFile(0) : 0; - if (file && id && file.type && file.type.match(/^image/i)) { - const imageId = `[img=${id}]`, - reader = new FileReader(); - - reader.onloadend = () => { - if (reader.result && 'wysiwyg' === editor.mode) { - try { - editor.setData(editor.getData().replace(imageId, ``)); - } catch (e) {} // eslint-disable-line no-empty - } - }; - - reader.readAsDataURL(file); - - dt.setData('text/html', imageId); - } - }); - - editor.on('instanceReady', onReady); - - return editor; - } - } - -})(window.rl); diff --git a/plugins/ckeditor/index.php b/plugins/ckeditor/index.php deleted file mode 100644 index fe68dcc9c..000000000 --- a/plugins/ckeditor/index.php +++ /dev/null @@ -1,41 +0,0 @@ -isDir()) { - \mkdir($path . DIRECTORY_SEPARATOR . $iterator->getSubPathName()); - } else { - \copy($item, $path . DIRECTORY_SEPARATOR . $iterator->getSubPathName()); - } - } - umask($old_mask); - } - - if (\is_file("{$path}/ckeditor.js")) { - $this->addCss('style.css'); - $this->addJs('ckeditor.js'); - } - } -} diff --git a/plugins/ckeditor/src/CHANGES.md b/plugins/ckeditor/src/CHANGES.md deleted file mode 100644 index ff58654f6..000000000 --- a/plugins/ckeditor/src/CHANGES.md +++ /dev/null @@ -1,1219 +0,0 @@ -CKEditor 4 Changelog -==================== - -## CKEditor 4.6.2 - -New Features: - -* [#16733](http://dev.ckeditor.com/ticket/16733): Added a new pastel color palette for the [Color Button](http://ckeditor.com/addon/colorbutton) plugin and a new [`config.colorButton_colorsPerRow`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-colorButton_colorsPerRow) configuration option for setting the number of rows in the color selector. -* [#16752](http://dev.ckeditor.com/ticket/16752): Added a new Azerbaijani localization. Thanks to the [Azerbaijani language team](https://www.transifex.com/ckeditor/teams/11143/az/)! -* [#13818](http://dev.ckeditor.com/ticket/13818): It is now possible to group [Widget](http://ckeditor.com/addon/widget) [style definitions](http://docs.ckeditor.com/#!/guide/dev_styles-section-widget-styles), so applying one style disables the other. - -Fixed Issues: - -* [#13446](http://dev.ckeditor.com/ticket/13446): [Chrome] Fixed: It is possible to type in an unfocused inline editor. -* [#14856](http://dev.ckeditor.com/ticket/14856): Fixed: [Font size and font family](http://ckeditor.com/addon/font) reset each other when modified at certain positions. -* [#16745](http://dev.ckeditor.com/ticket/16745): [Edge] Fixed: List items are lost when [pasted from Word](http://ckeditor.com/addon/pastefromword). -* [#16682](http://dev.ckeditor.com/ticket/16682): [Edge] Fixed: A list gets [pasted from Word](http://ckeditor.com/addon/pastefromword) as a set of paragraphs. Added the [`config.pasteFromWord_heuristicsEdgeList`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWord_heuristicsEdgeList) configuration option. -* [#10373](http://dev.ckeditor.com/ticket/10373): Fixed: Context menu items can be dragged into the editor. -* [#16728](http://dev.ckeditor.com/ticket/16728): [IE] Fixed: [Copy Formatting](http://ckeditor.com/addon/copyformatting) breaks the editor in Quirks Mode. -* [#16795](http://dev.ckeditor.com/ticket/16795): [IE] Fixed: [Copy Formatting](http://ckeditor.com/addon/copyformatting) breaks the editor in Compatibility Mode. -* [#16675](http://dev.ckeditor.com/ticket/16675): Fixed: Styles applied with [Copy Formatting](http://ckeditor.com/addon/copyformatting) to a single table cell are applied to the whole table. -* [#16753](http://dev.ckeditor.com/ticket/16753): Fixed: [`element.setSize`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.element-method-setSize) sets incorrect editor dimensions if the border width is represented as a fraction of pixels. -* [#16705](http://dev.ckeditor.com/ticket/16705): [Firefox] Fixed: Unable to paste images as Base64 strings when using [Clipboard](http://ckeditor.com/addon/clipboard). -* [#14869](http://dev.ckeditor.com/ticket/14869): Fixed: JavaScript error is thrown when trying to use [Find](http://ckeditor.com/addon/find) in a [`
`-based editor](http://ckeditor.com/addon/divarea). - -## CKEditor 4.6.1 - -New Features: - -* [#16639](http://dev.ckeditor.com/ticket/16639): The `callback` parameter in the [CKEDITOR.ajax.post](http://docs.ckeditor.com/#!/api/CKEDITOR.ajax-method-post) method became optional. - -Fixed Issues: - -* [#11064](http://dev.ckeditor.com/ticket/11064): [Blink, WebKit] Fixed: Cannot select all editor content when a widget or a non-editable element is the first or last element of the content. Also fixes this issue in the [Select All](http://ckeditor.com/addon/selectall) plugin. -* [#14755](http://dev.ckeditor.com/ticket/14755): [Blink, WebKit, IE8] Fixed: Browser hangs when a table is inserted in the place of a selected list with an empty last item. -* [#16624](http://dev.ckeditor.com/ticket/16624): Fixed: Improved the [Color Button](http://ckeditor.com/addon/colorbutton) plugin which will now normalize the CSS `background` property if it only contains a color value. This fixes missing background colors when using [Paste from Word](http://ckeditor.com/addon/pastefromword). -* [#16600](http://dev.ckeditor.com/ticket/16600): [Blink, WebKit] Fixed: Error thrown occasionally by an uninitialized editable for multiple CKEditor instances on the same page. - -## CKEditor 4.6 - -New Features: - -* [#14569](http://dev.ckeditor.com/ticket/14569): Added a new, flat, default CKEditor skin called [Moono-Lisa](http://ckeditor.com/addon/moono-lisa). Refreshed default colors available in the [Color Button](http://ckeditor.com/addon/colorbutton) plugin ([Text Color and Background Color](http://docs.ckeditor.com/#!/guide/dev_colorbutton) feature). -* [#14707](http://dev.ckeditor.com/ticket/14707): Added a new [Copy Formatting](http://ckeditor.com/addon/copyformatting) feature to enable easy copying of styles between your document parts. -* Introduced the completely rewritten [Paste from Word](http://ckeditor.com/addon/pastefromword) plugin: - * Backward incompatibility: The [`config.pasteFromWordRemoveFontStyles`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordRemoveFontStyles) option now defaults to `false`. This option will be deprecated in the future. Use [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_acf) to replicate the effect of setting it to `true`. - * Backward incompatibility: The [`config.pasteFromWordNumberedHeadingToList`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordNumberedHeadingToList) and [`config.pasteFromWordRemoveStyles`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordRemoveStyles) options were dropped and no longer have any effect on pasted content. - * Major improvements in preservation of list numbering, styling and indentation (nested lists with multiple levels). - * Major improvements in document structure parsing that fix plenty of issues with distorted or missing content after paste. -* Added new translation: Occitan. Thanks to [Cédric Valmary](https://totenoc.eu/)! -* [#10015](http://dev.ckeditor.com/ticket/10015): Keyboard shortcuts (relevant to the operating system in use) will now be displayed in tooltips and context menus. -* [#13794](http://dev.ckeditor.com/ticket/13794): The [Upload Image](http://ckeditor.com/addon/uploadimage) feature now uses `uploaded.width/height` if set. -* [#12541](http://dev.ckeditor.com/ticket/12541): Added the [Upload File](http://ckeditor.com/addon/uploadfile) plugin that lets you upload a file by drag&dropping it into the editor content. -* [#14449](http://dev.ckeditor.com/ticket/14449): Introduced the [Balloon Panel](http://ckeditor.com/addon/balloonpanel) plugin that lets you create stylish floating UI elements for the editor. -* [#12077](https://dev.ckeditor.com/ticket/12077): Added support for the HTML5 `download` attribute in link (``) elements. Selecting the "Force Download" checkbox in the [Link](http://ckeditor.com/addon/link) dialog will cause the linked file to be downloaded automatically. Thanks to [sbusse](https://github.com/sbusse)! -* [#13518](http://dev.ckeditor.com/ticket/13518): Introduced the [`additionalRequestParameters`](http://docs.ckeditor.com/#!/api/CKEDITOR.fileTools.uploadWidgetDefinition-property-additionalRequestParameters) property for file uploads to make it possible to send additional information about the uploaded file to the server. -* [#14889](http://dev.ckeditor.com/ticket/14889): Added the [`config.image2_altRequired`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image2_altRequired) option for the [Enhanced Image](http://ckeditor.com/addon/image2) plugin to allow making alternative text a mandatory field. Thanks to [Andrey Fedoseev](https://github.com/andreyfedoseev)! - -Fixed Issues: - -* [#9991](http://dev.ckeditor.com/ticket/9991): Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) should only normalize input data. -* [#7209](http://dev.ckeditor.com/ticket/7209): Fixed: Lists with 3 levels not [pasted from Word](http://ckeditor.com/addon/pastefromword) correctly. -* [#14335](http://dev.ckeditor.com/ticket/14335): Fixed: Pasting a numbered list starting with a value different from "1" from Microsoft Word does not work correctly. -* [#14542](http://dev.ckeditor.com/ticket/14542): Fixed: Copying a numbered list from Microsoft Word does not preserve list formatting. -* [#14544](http://dev.ckeditor.com/ticket/14544): Fixed: Copying a nested list from Microsoft Word results in an empty list. -* [#14660](http://dev.ckeditor.com/ticket/14660): Fixed: [Pasting text from Word](http://ckeditor.com/addon/pastefromword) breaks the styling in some cases. -* [#14867](http://dev.ckeditor.com/ticket/14867): [Firefox] Fixed: Text gets stripped when [pasting content from Word](http://ckeditor.com/addon/pastefromword). -* [#2507](http://dev.ckeditor.com/ticket/2507): Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) does not detect pasting a part of a paragraph. -* [#3336](http://dev.ckeditor.com/ticket/3336): Fixed: Extra blank row added on top of the content [pasted from Word](http://ckeditor.com/addon/pastefromword). -* [#6115](http://dev.ckeditor.com/ticket/6115): Fixed: When Right-to-Left text direction is applied to a table [pasted from Word](http://ckeditor.com/addon/pastefromword), borders are missing on one side. -* [#6342](http://dev.ckeditor.com/ticket/6342): Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) filters out a basic text style when it is [configured to use attributes](http://docs.ckeditor.com/#!/guide/dev_basicstyles-section-custom-basic-text-style-definition). -* [#6457](http://dev.ckeditor.com/ticket/6457): [IE] Fixed: [Pasting from Word](http://ckeditor.com/addon/pastefromword) is extremely slow. -* [#6789](http://dev.ckeditor.com/ticket/6789): Fixed: The `mso-list: ignore` style is not handled properly when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#7262](http://dev.ckeditor.com/ticket/7262): Fixed: Lists in preformatted body disappear when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#7662](http://dev.ckeditor.com/ticket/7662): [Opera] Fixed: Extra empty number/bullet shown in the editor body when editing a multi-level list [pasted from Word](http://ckeditor.com/addon/pastefromword). -* [#7807](http://dev.ckeditor.com/ticket/7807): Fixed: Last item in a list not converted to a `
  • ` element after [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#7950](http://dev.ckeditor.com/ticket/7950): [IE] Fixed: Content [from Word pasted](http://ckeditor.com/addon/pastefromword) differently than in other browsers. -* [#7982](http://dev.ckeditor.com/ticket/7982): Fixed: Multi-level lists get split into smaller ones when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#8231](http://dev.ckeditor.com/ticket/8231): [WebKit, Opera] Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) inserts empty paragraphs. -* [#8266](http://dev.ckeditor.com/ticket/8266): Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) inserts a blank line at the top. -* [#8341](http://dev.ckeditor.com/ticket/8341), [#7646](http://dev.ckeditor.com/ticket/7646): Fixed: Faulty removal of empty `` elements in [Paste from Word](http://ckeditor.com/addon/pastefromword) content cleanup breaking content formatting. -* [#8754](http://dev.ckeditor.com/ticket/8754): [Firefox] Fixed: Incorrect pasting of multiple nested lists in [Paste from Word](http://ckeditor.com/addon/pastefromword). -* [#8983](http://dev.ckeditor.com/ticket/8983): Fixed: Alignment lost when [pasting from Word](http://ckeditor.com/addon/pastefromword) with [`config.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode) set to [`CKEDITOR.ENTER_BR`](http://docs.ckeditor.com/#!/api/CKEDITOR-property-ENTER_BR). -* [#9331](http://dev.ckeditor.com/ticket/9331): [IE] Fixed: [Pasting text from Word](http://ckeditor.com/addon/pastefromword) creates a simple Caesar cipher. -* [#9422](http://dev.ckeditor.com/ticket/9422): Fixed: [Paste from Word](http://ckeditor.com/addon/pastefromword) leaves an unwanted `color:windowtext` style. -* [#10011](http://dev.ckeditor.com/ticket/10011): [IE9-10] Fixed: [`config.pasteFromWordRemoveFontStyles`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordRemoveFontStyles) is ignored under certain conditions. -* [#10643](http://dev.ckeditor.com/ticket/10643): Fixed: Differences between using Ctrl+V and pasting from the [Paste from Word](http://ckeditor.com/addon/pastefromword) dialog. -* [#10784](http://dev.ckeditor.com/ticket/10784): Fixed: Lines missing when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#11294](http://dev.ckeditor.com/ticket/11294): [IE10] Fixed: Font size is not preserved when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#11627](http://dev.ckeditor.com/ticket/11627): Fixed: Missing words when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#12784](http://dev.ckeditor.com/ticket/12784): Fixed: Bulleted list with custom bullets gets changed to a numbered list when [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#13174](http://dev.ckeditor.com/ticket/13174): Fixed: Data loss after [pasting from Word](http://ckeditor.com/addon/pastefromword). -* [#13828](http://dev.ckeditor.com/ticket/13828): Fixed: Widget classes should be added to the wrapper rather than the widget element. -* [#13829](http://dev.ckeditor.com/ticket/13829): Fixed: No class in [Widget](http://ckeditor.com/addon/widget) wrapper to identify the widget type. -* [#13519](http://dev.ckeditor.com/ticket/13519): Server response received when uploading files should be more flexible. - -Other Changes: - -* Updated [SCAYT](http://ckeditor.com/addon/scayt) (Spell Check As You Type) and [WebSpellChecker](http://ckeditor.com/addon/wsc) plugins: - * Support for the new default Moono-Lisa skin. - * [#121](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/121): Fixed: [Basic Styles](http://ckeditor.com/addon/basicstyles) do not work when SCAYT is enabled. - * [#125](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/125): Fixed: Inline styles are not continued when writing multiple lines of styled text with SCAYT enabled. - * [#127](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/127): Fixed: Uncaught TypeError after enabling SCAYT in the CKEditor `
    ` element. - * [#128](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/128): Fixed: Error thrown after enabling SCAYT caused by conflicts with RequireJS. - -## CKEditor 4.5.11 - -**Security Updates:** - -* [Severity: minor] Fixed the `target="_blank"` vulnerability reported by James Gaskell. - - Issue summary: If a victim had access to a spoofed version of ckeditor.com via HTTP (e.g. due to DNS spoofing, using a hacked public network or mailicious hotspot), then when using a link to the ckeditor.com website it was possible for the attacker to change the current URL of the opening page, even if the opening page was protected with SSL. - - An upgrade is recommended. - -New Features: - -* [#14747](http://dev.ckeditor.com/ticket/14747): The [Enhanced Image](http://ckeditor.com/addon/image2) caption now supports the link `target` attribute. -* [#7154](http://dev.ckeditor.com/ticket/7154): Added support for the "Display Text" field to the [Link](http://ckeditor.com/addon/link) dialog. Thanks to [Ryan Guill](https://github.com/ryanguill)! - -Fixed Issues: - -* [#13362](http://dev.ckeditor.com/ticket/13362): [Blink, WebKit] Fixed: Active widget element is not cached when it is losing focus and it is inside an editable element. -* [#13755](http://dev.ckeditor.com/ticket/13755): [Edge] Fixed: Pasting images does not work. -* [#13548](http://dev.ckeditor.com/ticket/13548): [IE] Fixed: Clicking the [elements path](http://ckeditor.com/addon/elementspath) disables Cut and Copy icons. -* [#13812](http://dev.ckeditor.com/ticket/13812): Fixed: When aborting file upload the placeholder for image is left. -* [#14659](http://dev.ckeditor.com/ticket/14659): [Blink] Fixed: Content scrolled to the top after closing the dialog in a [`
    `-based editor](http://ckeditor.com/addon/divarea). -* [#14825](http://dev.ckeditor.com/ticket/14825): [Edge] Fixed: Focusing the editor causes unwanted scrolling due to dropped support for the `setActive` method. - -## CKEditor 4.5.10 - -Fixed Issues: - -* [#10750](http://dev.ckeditor.com/ticket/10750): Fixed: The editor does not escape the `font-style` family property correctly, removing quotes and whitespace from font names. -* [#14413](http://dev.ckeditor.com/ticket/14413): Fixed: The [Auto Grow](http://ckeditor.com/addon/autogrow) plugin with the [`config.autoGrow_onStartup`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-autoGrow_onStartup) option set to `true` does not work properly for an editor that is not visible. -* [#14451](http://dev.ckeditor.com/ticket/14451): Fixed: Numeric element ID not escaped properly. Thanks to [Jakub Chalupa](https://github.com/chaluja7)! -* [#14590](http://dev.ckeditor.com/ticket/14590): Fixed: Additional line break appearing after inline elements when switching modes. Thanks to [dpidcock](https://github.com/dpidcock)! -* [#14539](https://dev.ckeditor.com/ticket/14539): Fixed: JAWS reads "selected Blank" instead of "selected " when selecting a widget. -* [#14701](http://dev.ckeditor.com/ticket/14701): Fixed: More precise labels for [Enhanced Image](http://ckeditor.com/addon/image2) and [Placeholder](http://ckeditor.com/addon/placeholder) widgets. -* [#14667](http://dev.ckeditor.com/ticket/14667): [IE] Fixed: Removing background color from selected text removes background color from the whole paragraph. -* [#14252](http://dev.ckeditor.com/ticket/14252): [IE] Fixed: Styles drop-down list does not always reflect the current style of the text line. -* [#14275](http://dev.ckeditor.com/ticket/14275): [IE9+] Fixed: `onerror` and `onload` events are not used in browsers it could have been used when loading scripts dynamically. - -## CKEditor 4.5.9 - -Fixed Issues: - -* [#10685](http://dev.ckeditor.com/ticket/10685): Fixed: Unreadable toolbar icons after updating to the new editor version. Fixed with [6876179](https://github.com/ckeditor/ckeditor-dev/commit/6876179db4ee97e786b07b8fd72e6b4120732185) in [ckeditor-dev](https://github.com/ckeditor/ckeditor-dev) and [6c9189f4](https://github.com/ckeditor/ckeditor-presets/commit/6c9189f46392d2c126854fe8889b820b8c76d291) in [ckeditor-presets](https://github.com/ckeditor/ckeditor-presets). -* [#14573](https://dev.ckeditor.com/ticket/14573): Fixed: Missing [Widget](http://ckeditor.com/addon/widget) drag handler CSS when there are multiple editor instances. -* [#14620](https://dev.ckeditor.com/ticket/14620): Fixed: Setting both the `min-height` style for the `` element and the `height` style for the `` element breaks the [Auto Grow](http://ckeditor.com/addon/autogrow) plugin. -* [#14538](http://dev.ckeditor.com/ticket/14538): Fixed: Keyboard focus goes into an embedded ` + + +
    +
    +
    + +
    + + +
    + +
    +'.$_['BaseAppBootScript'].$_['BaseLanguage'].' +'; diff --git a/integrations/owncloud/snappymail/templates/personal_settings.php b/integrations/owncloud/snappymail/templates/personal_settings.php new file mode 100644 index 000000000..93a86c744 --- /dev/null +++ b/integrations/owncloud/snappymail/templates/personal_settings.php @@ -0,0 +1,21 @@ +
    +
    + +
    +

    t('SnappyMail Webmail'); ?>

    +

    + t('Enter an email and password to auto-login to SnappyMail.'); ?> +

    +

    + + + + + +    +

    +
    +
    +
    diff --git a/release.php b/release.php index ccd3b1f21..aae2c8b42 100755 --- a/release.php +++ b/release.php @@ -2,7 +2,7 @@ version}-nextcloud.tar.gz"), $return_var); } + if (isset($options['owncloud'])) { + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + .escapeshellarg("{$destPath}snappymail-{$package->version}-owncloud.tar.gz"), $return_var); + } if (isset($options['cpanel'])) { passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' .escapeshellarg("{$destPath}snappymail-{$package->version}-cpanel.tar.gz"), $return_var); From 1c9b53eb2581a2fbcd3c69c1a5a39c02acc6a646 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 12:58:38 +0100 Subject: [PATCH 0388/1012] Remove 'set-version' argument for release.php --- build/nextcloud.php | 4 ++++ release.php | 9 +-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build/nextcloud.php b/build/nextcloud.php index 10f2595eb..1c58912ed 100755 --- a/build/nextcloud.php +++ b/build/nextcloud.php @@ -11,6 +11,10 @@ $nc_destination = "{$destPath}snappymail-{$package->version}-nextcloud.tar"; $nc_tar = new PharData($nc_destination); $hashes = []; +file_put_contents(ROOT_DIR . '/integrations/nextcloud/snappymail/VERSION', $package->version); +$file = ROOT_DIR . '/integrations/nextcloud/snappymail/appinfo/info.xml'; +file_put_contents($file, preg_replace('/[^<]*{$package->version}<", file_get_contents($file))); + $nc_tar->buildFromDirectory('./integrations/nextcloud', "@integrations/nextcloud/snappymail/@"); $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('integrations/nextcloud/snappymail')); foreach ($files as $file) { diff --git a/release.php b/release.php index aae2c8b42..70a8c3624 100755 --- a/release.php +++ b/release.php @@ -2,7 +2,7 @@ [^<]*{$package // docker $file = __DIR__ . '/.docker/release/files/usr/local/include/application.ini'; file_put_contents($file, preg_replace('/current = "[0-9.]+"/', "current = \"{$package->version}\"", file_get_contents($file))); -// nextcloud -$file = __DIR__ . '/integrations/nextcloud/snappymail/appinfo/info.xml'; -file_put_contents($file, preg_replace('/[^<]*{$package->version}<", file_get_contents($file))); // virtualmin $file = __DIR__ . '/integrations/virtualmin/snappymail.pl'; file_put_contents($file, preg_replace('/return \\( "[0-9]+\\.[0-9]+\\.[0-9]+" \\)/', "return ( \"{$package->version}\" )", file_get_contents($file))); -if (isset($options['set-version'])) { - exit; -} - // Arch User Repository // https://aur.archlinux.org/packages/snappymail/ $options['aur'] = isset($options['aur']); From 8fdd0407db17db31d6d7685f1316c34804a81803 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 13:20:27 +0100 Subject: [PATCH 0389/1012] move release.php to cli/release.php --- build/plugins.php | 1 - cli/release.php | 288 ++++++++++++++++++++++++++++++++++++++++++++++ release.php | 286 +-------------------------------------------- 3 files changed, 289 insertions(+), 286 deletions(-) create mode 100755 cli/release.php diff --git a/build/plugins.php b/build/plugins.php index 8242ef74a..ab1b5b64d 100755 --- a/build/plugins.php +++ b/build/plugins.php @@ -1,5 +1,4 @@ version}", file_get_contents($file))); +$file = ROOT_DIR . '/integrations/cloudron/DESCRIPTION.md'; +file_put_contents($file, preg_replace('/[^<]*{$package->version}<", file_get_contents($file))); +// docker +$file = ROOT_DIR . '/.docker/release/files/usr/local/include/application.ini'; +file_put_contents($file, preg_replace('/current = "[0-9.]+"/', "current = \"{$package->version}\"", file_get_contents($file))); +// virtualmin +$file = ROOT_DIR . '/integrations/virtualmin/snappymail.pl'; +file_put_contents($file, preg_replace('/return \\( "[0-9]+\\.[0-9]+\\.[0-9]+" \\)/', "return ( \"{$package->version}\" )", file_get_contents($file))); + +// Arch User Repository +// https://aur.archlinux.org/packages/snappymail/ +$options['aur'] = isset($options['aur']); + +// Docker build +$options['docker'] = isset($options['docker']); +if ($options['docker'] && !($docker = trim(`which docker`))) { + exit('docker not found'); +} +if ($options['docker'] && $options['aur']) { + exit('Conflict between docker and aur'); +} + +$destPath = "build/dist/releases/webmail/{$package->version}/"; +is_dir($destPath) || mkdir($destPath, 0777, true); + +$zip_destination = "{$destPath}snappymail-{$package->version}.zip"; +$tar_destination = "{$destPath}snappymail-{$package->version}.tar"; + +@unlink($zip_destination); +@unlink($tar_destination); +@unlink("{$tar_destination}.gz"); + +if (!isset($options['skip-gulp'])) { + echo "\x1b[33;1m === Gulp === \x1b[0m\n"; + passthru($gulp, $return_var); + if ($return_var) { + exit("gulp failed with error code {$return_var}\n"); + } + + $cmddir = escapeshellcmd(ROOT_DIR) . '/snappymail/v/0.0.0/static'; + + if ($gzip = trim(`which gzip`)) { + echo "\x1b[33;1m === Gzip *.js and *.css === \x1b[0m\n"; + passthru("{$gzip} -k --best {$cmddir}/js/*.js"); + passthru("{$gzip} -k --best {$cmddir}/js/min/*.js"); + passthru("{$gzip} -k --best {$cmddir}/css/admin*.css"); + passthru("{$gzip} -k --best {$cmddir}/css/app*.css"); + unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/boot.js.gz'); + unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/min/boot.min.js.gz'); + } + + if ($brotli = trim(`which brotli`)) { + echo "\x1b[33;1m === Brotli *.js and *.css === \x1b[0m\n"; + passthru("{$brotli} -k --best {$cmddir}/js/*.js"); + passthru("{$brotli} -k --best {$cmddir}/js/min/*.js"); + passthru("{$brotli} -k --best {$cmddir}/css/admin*.css"); + passthru("{$brotli} -k --best {$cmddir}/css/app*.css"); + unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/boot.js.br'); + unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/min/boot.min.js.br'); + } +} + +// Temporary rename folder to speed up PharData +//if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")){ +if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")) { + exit('Failed to temporary rename snappymail/v/0.0.0'); +} +register_shutdown_function(function(){ + // Rename folder back to original + @rename("snappymail/v/{$GLOBALS['package']->version}", 'snappymail/v/0.0.0'); +}); + +echo "\x1b[33;1m === Zip/Tar === \x1b[0m\n"; + +$zip = new ZipArchive(); +if (!$zip->open($zip_destination, ZIPARCHIVE::CREATE)) { + exit("Failed to create {$zip_destination}"); +} + +$tar = new PharData($tar_destination); + +$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST); +foreach ($files as $file) { + $file = str_replace('\\', '/', $file); + //echo "{$file}\n"; + // Ignore "." and ".." folders + if (!in_array(substr($file, strrpos($file, '/')+1), array('.', '..'))) { + if (is_dir($file)) { + $zip->addEmptyDir($file); + } else if (is_file($file)) { + $zip->addFile($file); + } + } +} + +if ($options['docker']) { + $tar->buildFromDirectory('./snappymail/', "@v/{$package->version}@"); +} else { + $tar->buildFromDirectory('./', "@snappymail/v/{$package->version}@"); +} + +//$zip->addFile('cli/upgrade.sh'); +//$tar->addFile('cli/upgrade.sh'); + +$zip->addFile('data/.htaccess'); +$tar->addFile('data/.htaccess'); + +$zip->addFromString('data/VERSION', $package->version); +$tar->addFromString('data/VERSION', $package->version); + +$zip->addFile('data/README.md'); +$tar->addFile('data/README.md'); + +if ($options['aur']) { + $data = 'addFromString('include.php', $data); + $tar->addFromString('include.php', $data); +} else { + $zip->addFile('_include.php'); + $tar->addFile('_include.php'); +} + +$zip->addFile('.htaccess'); +$tar->addFile('.htaccess'); + +$index = file_get_contents('index.php'); +$index = str_replace('0.0.0', $package->version, $index); +$zip->addFromString('index.php', $index); +$tar->addFromString('index.php', $index); + +$zip->addFile('README.md'); +$tar->addFile('README.md'); + +$data = file_get_contents('dev/serviceworker.js'); +//$data = file_get_contents('snappymail/v/0.0.0/static/js/min/serviceworker.min.js'); +$zip->addFromString('serviceworker.js', $data); +$tar->addFromString('serviceworker.js', $data); + +$zip->close(); + +$tar->compress(Phar::GZ); +unlink($tar_destination); +$tar_destination .= '.gz'; + +echo "{$zip_destination} created\n{$tar_destination} created\n"; + +if (isset($options['nextcloud'])) { + require(ROOT_DIR . '/build/nextcloud.php'); +} + +if (isset($options['owncloud'])) { + require(ROOT_DIR . '/build/owncloud.php'); +} + +if (isset($options['cpanel'])) { + require(ROOT_DIR . '/build/cpanel.php'); +} + +rename("snappymail/v/{$package->version}", 'snappymail/v/0.0.0'); + +file_put_contents("{$destPath}core.json", '{ + "version": "'.$package->version.'", + "file": "../latest.tar.gz", + "warnings": [] +}'); + +// Arch User Repository +if ($options['aur']) { + // extension_loaded('blake2') + if (!function_exists('b2sum') && $b2sum = trim(`which b2sum`)) { + function b2sum($file) { + $file = escapeshellarg($file); + exec("b2sum --binary {$file} 2>&1", $output, $exitcode); + $output = explode(' ', implode("\n", $output)); + return $output[0]; + } + } + + $b2sums = function_exists('b2sum') ? [ + b2sum($tar_destination), + b2sum(ROOT_DIR . '/build/arch/snappymail.sysusers'), + b2sum(ROOT_DIR . '/build/arch/snappymail.tmpfiles') + ] : []; + + file_put_contents('build/arch/.SRCINFO', 'pkgbase = snappymail + pkgdesc = modern PHP webmail client + pkgver = '.$package->version.' + pkgrel = 1 + url = https://github.com/the-djmaze/snappymail + arch = any + license = AGPL3 + makedepends = php + makedepends = nodejs + makedepends = yarn + makedepends = gulp + depends = php-fpm + optdepends = mariadb: storage backend for contacts + optdepends = php-pgsql: storage backend for contacts + optdepends = php-sqlite: storage backend for contacts + source = snappymail-'.$package->version.'.tar.gz::https://github.com/the-djmaze/snappymail/archive/v'.$package->version.'.tar.gz + source = snappymail.sysusers + source = snappymail.tmpfiles + b2sums = '.implode("\n b2sums = ", $b2sums).' + +pkgname = snappymail +'); + + $file = ROOT_DIR . '/build/arch/PKGBUILD'; + if (is_file($file)) { + $PKGBUILD = file_get_contents($file); + $PKGBUILD = preg_replace('/pkgver=[0-9.]+/', "pkgver={$package->version}", $PKGBUILD); + $PKGBUILD = preg_replace('/b2sums=\\([^)]+\\)/s', "b2sums=('".implode("'\n '", $b2sums)."')", $PKGBUILD); + file_put_contents($file, $PKGBUILD); + } +} +// Debian Repository +else if (isset($options['debian'])) { + require(ROOT_DIR . '/build/deb.php'); +} +// Docker build +else if ($options['docker']) { + echo "\x1b[33;1m === Docker === \x1b[0m\n"; + $zip_filename = "snappymail-{$package->version}.zip"; + copy($zip_destination, "./.docker/release/{$zip_filename}"); + if ($docker) { + passthru("{$docker} build --pull " . ROOT_DIR . "/.docker/release/ --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}"); + } else { + echo "Docker not installed!\n"; + } +} + +if (isset($options['sign'])) { + echo "\x1b[33;1m === PGP Sign === \x1b[0m\n"; + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($tar_destination), $return_var); + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($zip_destination), $return_var); + if (isset($options['nextcloud'])) { + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + .escapeshellarg("{$destPath}snappymail-{$package->version}-nextcloud.tar.gz"), $return_var); + } + if (isset($options['owncloud'])) { + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + .escapeshellarg("{$destPath}snappymail-{$package->version}-owncloud.tar.gz"), $return_var); + } + if (isset($options['cpanel'])) { + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + .escapeshellarg("{$destPath}snappymail-{$package->version}-cpanel.tar.gz"), $return_var); + } + if (isset($options['debian'])) { + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/" . basename(DEB_DEST_DIR.'.deb')), $return_var); + // https://github.com/the-djmaze/snappymail/issues/185#issuecomment-1059420588 + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 --clearsign --output ' + . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/InRelease") . ' ' + . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var); + passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 -abs --output ' + . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release.gpg") . ' ' + . escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var); + } +} diff --git a/release.php b/release.php index 70a8c3624..4bef9861f 100755 --- a/release.php +++ b/release.php @@ -1,287 +1,3 @@ #!/usr/bin/php version}", file_get_contents($file))); -$file = __DIR__ . '/integrations/cloudron/DESCRIPTION.md'; -file_put_contents($file, preg_replace('/[^<]*{$package->version}<", file_get_contents($file))); -// docker -$file = __DIR__ . '/.docker/release/files/usr/local/include/application.ini'; -file_put_contents($file, preg_replace('/current = "[0-9.]+"/', "current = \"{$package->version}\"", file_get_contents($file))); -// virtualmin -$file = __DIR__ . '/integrations/virtualmin/snappymail.pl'; -file_put_contents($file, preg_replace('/return \\( "[0-9]+\\.[0-9]+\\.[0-9]+" \\)/', "return ( \"{$package->version}\" )", file_get_contents($file))); - -// Arch User Repository -// https://aur.archlinux.org/packages/snappymail/ -$options['aur'] = isset($options['aur']); - -// Docker build -$options['docker'] = isset($options['docker']); -if ($options['docker'] && !($docker = trim(`which docker`))) { - exit('docker not found'); -} -if ($options['docker'] && $options['aur']) { - exit('Conflict between docker and aur'); -} - -$destPath = "build/dist/releases/webmail/{$package->version}/"; -is_dir($destPath) || mkdir($destPath, 0777, true); - -$zip_destination = "{$destPath}snappymail-{$package->version}.zip"; -$tar_destination = "{$destPath}snappymail-{$package->version}.tar"; - -@unlink($zip_destination); -@unlink($tar_destination); -@unlink("{$tar_destination}.gz"); - -if (!isset($options['skip-gulp'])) { - echo "\x1b[33;1m === Gulp === \x1b[0m\n"; - passthru($gulp, $return_var); - if ($return_var) { - exit("gulp failed with error code {$return_var}\n"); - } - - $cmddir = escapeshellcmd(__DIR__) . '/snappymail/v/0.0.0/static'; - - if ($gzip = trim(`which gzip`)) { - echo "\x1b[33;1m === Gzip *.js and *.css === \x1b[0m\n"; - passthru("{$gzip} -k --best {$cmddir}/js/*.js"); - passthru("{$gzip} -k --best {$cmddir}/js/min/*.js"); - passthru("{$gzip} -k --best {$cmddir}/css/admin*.css"); - passthru("{$gzip} -k --best {$cmddir}/css/app*.css"); - unlink(__DIR__ . '/snappymail/v/0.0.0/static/js/boot.js.gz'); - unlink(__DIR__ . '/snappymail/v/0.0.0/static/js/min/boot.min.js.gz'); - } - - if ($brotli = trim(`which brotli`)) { - echo "\x1b[33;1m === Brotli *.js and *.css === \x1b[0m\n"; - passthru("{$brotli} -k --best {$cmddir}/js/*.js"); - passthru("{$brotli} -k --best {$cmddir}/js/min/*.js"); - passthru("{$brotli} -k --best {$cmddir}/css/admin*.css"); - passthru("{$brotli} -k --best {$cmddir}/css/app*.css"); - unlink(__DIR__ . '/snappymail/v/0.0.0/static/js/boot.js.br'); - unlink(__DIR__ . '/snappymail/v/0.0.0/static/js/min/boot.min.js.br'); - } -} - -// Temporary rename folder to speed up PharData -//if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")){ -if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")) { - exit('Failed to temporary rename snappymail/v/0.0.0'); -} -register_shutdown_function(function(){ - // Rename folder back to original - @rename("snappymail/v/{$GLOBALS['package']->version}", 'snappymail/v/0.0.0'); -}); - -echo "\x1b[33;1m === Zip/Tar === \x1b[0m\n"; - -$zip = new ZipArchive(); -if (!$zip->open($zip_destination, ZIPARCHIVE::CREATE)) { - exit("Failed to create {$zip_destination}"); -} - -$tar = new PharData($tar_destination); - -$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST); -foreach ($files as $file) { - $file = str_replace('\\', '/', $file); - //echo "{$file}\n"; - // Ignore "." and ".." folders - if (!in_array(substr($file, strrpos($file, '/')+1), array('.', '..'))) { - if (is_dir($file)) { - $zip->addEmptyDir($file); - } else if (is_file($file)) { - $zip->addFile($file); - } - } -} - -if ($options['docker']) { - $tar->buildFromDirectory('./snappymail/', "@v/{$package->version}@"); -} else { - $tar->buildFromDirectory('./', "@snappymail/v/{$package->version}@"); -} - -//$zip->addFile('cli/upgrade.sh'); -//$tar->addFile('cli/upgrade.sh'); - -$zip->addFile('data/.htaccess'); -$tar->addFile('data/.htaccess'); - -$zip->addFromString('data/VERSION', $package->version); -$tar->addFromString('data/VERSION', $package->version); - -$zip->addFile('data/README.md'); -$tar->addFile('data/README.md'); - -if ($options['aur']) { - $data = 'addFromString('include.php', $data); - $tar->addFromString('include.php', $data); -} else { - $zip->addFile('_include.php'); - $tar->addFile('_include.php'); -} - -$zip->addFile('.htaccess'); -$tar->addFile('.htaccess'); - -$index = file_get_contents('index.php'); -$index = str_replace('0.0.0', $package->version, $index); -$zip->addFromString('index.php', $index); -$tar->addFromString('index.php', $index); - -$zip->addFile('README.md'); -$tar->addFile('README.md'); - -$data = file_get_contents('dev/serviceworker.js'); -//$data = file_get_contents('snappymail/v/0.0.0/static/js/min/serviceworker.min.js'); -$zip->addFromString('serviceworker.js', $data); -$tar->addFromString('serviceworker.js', $data); - -$zip->close(); - -$tar->compress(Phar::GZ); -unlink($tar_destination); -$tar_destination .= '.gz'; - -echo "{$zip_destination} created\n{$tar_destination} created\n"; - -if (isset($options['nextcloud'])) { - require(__DIR__ . '/build/nextcloud.php'); -} - -if (isset($options['owncloud'])) { - require(__DIR__ . '/build/owncloud.php'); -} - -if (isset($options['cpanel'])) { - require(__DIR__ . '/build/cpanel.php'); -} - -rename("snappymail/v/{$package->version}", 'snappymail/v/0.0.0'); - -file_put_contents("{$destPath}core.json", '{ - "version": "'.$package->version.'", - "file": "../latest.tar.gz", - "warnings": [] -}'); - -// Arch User Repository -if ($options['aur']) { - // extension_loaded('blake2') - if (!function_exists('b2sum') && $b2sum = trim(`which b2sum`)) { - function b2sum($file) { - $file = escapeshellarg($file); - exec("b2sum --binary {$file} 2>&1", $output, $exitcode); - $output = explode(' ', implode("\n", $output)); - return $output[0]; - } - } - - $b2sums = function_exists('b2sum') ? [ - b2sum($tar_destination), - b2sum(__DIR__ . '/build/arch/snappymail.sysusers'), - b2sum(__DIR__ . '/build/arch/snappymail.tmpfiles') - ] : []; - - file_put_contents('build/arch/.SRCINFO', 'pkgbase = snappymail - pkgdesc = modern PHP webmail client - pkgver = '.$package->version.' - pkgrel = 1 - url = https://github.com/the-djmaze/snappymail - arch = any - license = AGPL3 - makedepends = php - makedepends = nodejs - makedepends = yarn - makedepends = gulp - depends = php-fpm - optdepends = mariadb: storage backend for contacts - optdepends = php-pgsql: storage backend for contacts - optdepends = php-sqlite: storage backend for contacts - source = snappymail-'.$package->version.'.tar.gz::https://github.com/the-djmaze/snappymail/archive/v'.$package->version.'.tar.gz - source = snappymail.sysusers - source = snappymail.tmpfiles - b2sums = '.implode("\n b2sums = ", $b2sums).' - -pkgname = snappymail -'); - - $file = __DIR__ . '/build/arch/PKGBUILD'; - if (is_file($file)) { - $PKGBUILD = file_get_contents($file); - $PKGBUILD = preg_replace('/pkgver=[0-9.]+/', "pkgver={$package->version}", $PKGBUILD); - $PKGBUILD = preg_replace('/b2sums=\\([^)]+\\)/s', "b2sums=('".implode("'\n '", $b2sums)."')", $PKGBUILD); - file_put_contents($file, $PKGBUILD); - } -} -// Debian Repository -else if (isset($options['debian'])) { - require(__DIR__ . '/build/deb.php'); -} -// Docker build -else if ($options['docker']) { - echo "\x1b[33;1m === Docker === \x1b[0m\n"; - $zip_filename = "snappymail-{$package->version}.zip"; - copy($zip_destination, "./.docker/release/{$zip_filename}"); - if ($docker) { - passthru("{$docker} build --pull " . __DIR__ . "/.docker/release/ --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}"); - } else { - echo "Docker not installed!\n"; - } -} - -if (isset($options['sign'])) { - echo "\x1b[33;1m === PGP Sign === \x1b[0m\n"; - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($tar_destination), $return_var); - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($zip_destination), $return_var); - if (isset($options['nextcloud'])) { - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' - .escapeshellarg("{$destPath}snappymail-{$package->version}-nextcloud.tar.gz"), $return_var); - } - if (isset($options['owncloud'])) { - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' - .escapeshellarg("{$destPath}snappymail-{$package->version}-owncloud.tar.gz"), $return_var); - } - if (isset($options['cpanel'])) { - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' - .escapeshellarg("{$destPath}snappymail-{$package->version}-cpanel.tar.gz"), $return_var); - } - if (isset($options['debian'])) { - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' - . escapeshellarg(__DIR__ . "/build/dist/releases/webmail/{$package->version}/" . basename(DEB_DEST_DIR.'.deb')), $return_var); - // https://github.com/the-djmaze/snappymail/issues/185#issuecomment-1059420588 - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 --clearsign --output ' - . escapeshellarg(__DIR__ . "/build/dist/releases/webmail/{$package->version}/InRelease") . ' ' - . escapeshellarg(__DIR__ . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var); - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 -abs --output ' - . escapeshellarg(__DIR__ . "/build/dist/releases/webmail/{$package->version}/Release.gpg") . ' ' - . escapeshellarg(__DIR__ . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var); - } -} +require __DIR__ . '/cli/release.php'; From 1f9b3ec699f68139a66496b79c6a5e7d143de85b Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 13:26:00 +0100 Subject: [PATCH 0390/1012] Fix Nextcloud .htaccess workaround --- build/nextcloud.php | 7 +++++-- build/owncloud.php | 17 ++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/build/nextcloud.php b/build/nextcloud.php index 1c58912ed..b36a8fbee 100755 --- a/build/nextcloud.php +++ b/build/nextcloud.php @@ -21,7 +21,6 @@ foreach ($files as $file) { if (is_file($file)) { $name = str_replace('\\', '/', $file); $name = str_replace('integrations/nextcloud/snappymail/', '', $name); - $name = str_replace('.htaccess', '_htaccess', $name); $hashes[$name] = hash_file('sha512', $file); } } @@ -35,6 +34,11 @@ foreach ($files as $file) { $hashes["app/{$newFile}"] = hash_file('sha512', $file); } } +$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/app/.htaccess"); +$nc_tar->addFile("snappymail/v/{$package->version}/app/.htaccess", "snappymail/app/snappymail/v/{$package->version}/app/_htaccess"); +$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/static/.htaccess"); +$nc_tar->addFile("snappymail/v/{$package->version}/static/.htaccess", "snappymail/app/snappymail/v/{$package->version}/static/_htaccess"); + /* $nc_tar->addFile('data/.htaccess'); $nc_tar->addFromString('data/VERSION', $package->version); @@ -62,7 +66,6 @@ $hashes['app/serviceworker.js'] = hash('sha512', $data); spl_autoload_register(function($name){ $file = __DIR__ . '/' . str_replace('\\', '/', $name) . '.php'; - echo "{$file}\n"; require $file; }); diff --git a/build/owncloud.php b/build/owncloud.php index 8f6510157..5c422270d 100755 --- a/build/owncloud.php +++ b/build/owncloud.php @@ -21,7 +21,6 @@ foreach ($files as $file) { if (is_file($file)) { $name = str_replace('\\', '/', $file); $name = str_replace('integrations/owncloud/snappymail/', '', $name); - $name = str_replace('.htaccess', '_htaccess', $name); $hashes[$name] = hash_file('sha512', $file); } } @@ -35,6 +34,11 @@ foreach ($files as $file) { $hashes["app/{$newFile}"] = hash_file('sha512', $file); } } +$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/app/.htaccess"); +$nc_tar->addFile("snappymail/v/{$package->version}/app/.htaccess", "snappymail/app/snappymail/v/{$package->version}/app/_htaccess"); +$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/static/.htaccess"); +$nc_tar->addFile("snappymail/v/{$package->version}/static/.htaccess", "snappymail/app/snappymail/v/{$package->version}/static/_htaccess"); + /* $nc_tar->addFile('data/.htaccess'); $nc_tar->addFromString('data/VERSION', $package->version); @@ -60,13 +64,12 @@ $data = file_get_contents('dev/serviceworker.js'); $nc_tar->addFromString('snappymail/app/serviceworker.js', $data); $hashes['app/serviceworker.js'] = hash('sha512', $data); -spl_autoload_register(function($name){ - $file = __DIR__ . '/' . str_replace('\\', '/', $name) . '.php'; - echo "{$file}\n"; - require $file; -}); - if ($cert_dir) { + spl_autoload_register(function($name){ + $file = __DIR__ . '/' . str_replace('\\', '/', $name) . '.php'; + require $file; + }); + ksort($hashes); $cert = file_get_contents($cert_dir.'/snappymail.crt'); $rsa = new \phpseclib\Crypt\RSA(); From 8bd466a6562062ef6f02da0df59371253c817202 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 13:26:08 +0100 Subject: [PATCH 0391/1012] Resolve #824 --- integrations/nextcloud/snappymail/css/embed.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/integrations/nextcloud/snappymail/css/embed.css b/integrations/nextcloud/snappymail/css/embed.css index 150a27fe9..d790145b9 100644 --- a/integrations/nextcloud/snappymail/css/embed.css +++ b/integrations/nextcloud/snappymail/css/embed.css @@ -98,8 +98,7 @@ body > header ul { a.selectable { margin: 2px; - height: 38px !important; - line-height: 38px !important; + line-height: inherit !important; border-radius: var(--border-radius-pill); } From becdf29120a64082ae2a1f7cd1285c2939c2f728 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 13:50:38 +0100 Subject: [PATCH 0392/1012] Changes for #815 --- dev/Styles/User/MessageList.less | 1 + dev/View/User/MailBox/MessageList.js | 19 +++++++++++++------ .../templates/Views/User/MailMessageList.html | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/dev/Styles/User/MessageList.less b/dev/Styles/User/MessageList.less index 7026f6b5f..56b3a822f 100644 --- a/dev/Styles/User/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -31,6 +31,7 @@ html.rl-no-preview-pane { .groupLabel { background:rgba(128,128,128,0.2); + cursor: pointer; font-size: 12px; line-height: 2; opacity: 0.6; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 46dbd75cf..0a17250c3 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -164,6 +164,7 @@ export class MailMessageList extends AbstractViewRight { current = { id: email, label: msg.from[0].toLine(), + search: 'from=' + msg.from[0].email, messages: [] }; list.push(current); @@ -175,23 +176,25 @@ export class MailMessageList extends AbstractViewRight { rtf = Intl.RelativeTimeFormat ? new Intl.RelativeTimeFormat(doc.documentElement.lang, { numeric: "auto" }) : 0; MessagelistUserStore.forEach(msg => { - let date = (new Date(msg.dateTimeStampInUTC() * 1000)), - ymd = Ymd(date); + let dt = (new Date(msg.dateTimeStampInUTC() * 1000)), + date, + ymd = Ymd(dt); if (!current || ymd != current.id) { if (rtf && today == ymd) { date = rtf.format(0, 'day'); } else if (rtf && today - 1 == ymd) { date = rtf.format(-1, 'day'); // } else if (today - 7 < ymd) { -// date = date.format({weekday: 'long'}); -// date = date.format({dateStyle: 'full'},0,LanguageStore.hourCycle()); +// date = dt.format({weekday: 'long'}); +// date = dt.format({dateStyle: 'full'},0,LanguageStore.hourCycle()); } else { -// date = date.format({dateStyle: 'medium'},0,LanguageStore.hourCycle()); - date = date.format({dateStyle: 'full'},0,LanguageStore.hourCycle()); +// date = dt.format({dateStyle: 'medium'},0,LanguageStore.hourCycle()); + date = dt.format({dateStyle: 'full'},0,LanguageStore.hourCycle()); } current = { id: ymd, label: date, + search: 'on=' + dt.getFullYear() + '-' + pad2(1 + dt.getMonth()) + '-' + pad2(dt.getDate()), messages: [] }; list.push(current); @@ -815,6 +818,10 @@ export class MailMessageList extends AbstractViewRight { showScreenPopup(AdvancedSearchPopupView, [MessagelistUserStore.mainSearch()]); } + groupSearchClick(group) { + group.search && MessagelistUserStore.mainSearch(group.search); + } + quotaTooltip() { return i18n('MESSAGE_LIST/QUOTA_SIZE', { SIZE: FileInfo.friendlySize(FolderUserStore.quotaUsage()), 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 57d9fe607..5597bf0b9 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 @@ -130,8 +130,8 @@
    -
    -
    +
    +
    From cfd9c11db6e11526a54926c6bf8404bca8d42283 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 13:52:46 +0100 Subject: [PATCH 0393/1012] Moved sort options "FROM" direct below "DATE" for better understanding --- .../app/templates/Views/User/MailMessageList.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 5597bf0b9..d13027a03 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 @@ -81,6 +81,12 @@
  • + +
  • + +
  • @@ -93,12 +99,6 @@
  • - -
  • - -
  • From 0e5ae38c58516276f2af0deee5746df7ff568717 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 14:15:09 +0100 Subject: [PATCH 0394/1012] More changes for #815 --- dev/View/User/MailBox/MessageList.js | 6 +++++- .../v/0.0.0/app/templates/Views/User/MailMessageList.html | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 0a17250c3..104876975 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -818,10 +818,14 @@ export class MailMessageList extends AbstractViewRight { showScreenPopup(AdvancedSearchPopupView, [MessagelistUserStore.mainSearch()]); } - groupSearchClick(group) { + groupSearch(group) { group.search && MessagelistUserStore.mainSearch(group.search); } + groupCheck(group) { + group.messages.forEach(message => message.checked(!message.checked())); + } + quotaTooltip() { return i18n('MESSAGE_LIST/QUOTA_SIZE', { SIZE: FileInfo.friendlySize(FolderUserStore.quotaUsage()), 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 d13027a03..e3458780b 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 @@ -131,7 +131,7 @@
    -
    +
    From 19c0c5c98e6bb82eb68d2153197e088d57d03db1 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 15:11:17 +0100 Subject: [PATCH 0395/1012] Added DAV path error to log for #822 --- .../Providers/AddressBook/CardDAV.php | 201 ++++++------------ 1 file changed, 63 insertions(+), 138 deletions(-) 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 1ca58b5c1..eb03e96f3 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 @@ -42,16 +42,12 @@ trait CardDAV /** * find every *.vcf with empty */ - if (\is_array($aResponse)) - { + if (\is_array($aResponse)) { $mResult = array(); - foreach ($aResponse as $sKey => $aItem) - { + foreach ($aResponse as $sKey => $aItem) { $sKey = \rtrim(\trim($sKey), '\\/'); - if (!empty($sKey) && is_array($aItem)) - { - if (isset($aItem['{DAV:}getetag'])) - { + if (!empty($sKey) && is_array($aItem)) { + if (isset($aItem['{DAV:}getetag'])) { $aMatch = array(); if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1]) && !static::hasDAVCollection($aItem)) @@ -68,14 +64,11 @@ trait CardDAV 'changed' => 0 ); - if (isset($aItem['{DAV:}getlastmodified'])) - { + if (isset($aItem['{DAV:}getlastmodified'])) { $mResult[$sKeyID]['lastmodified'] = $aItem['{DAV:}getlastmodified']; $mResult[$sKeyID]['changed'] = \MailSo\Base\DateTimeHelper::ParseRFC2822DateString( $aItem['{DAV:}getlastmodified']); - } - else - { + } else { $mResult[$sKeyID]['changed'] = \MailSo\Base\DateTimeHelper::TryToParseSpecEtagFormat($mResult[$sKeyID]['etag']); $mResult[$sKeyID]['lastmodified'] = 0 < $mResult[$sKeyID]['changed'] ? \gmdate('c', $mResult[$sKeyID]['changed']) : ''; @@ -98,26 +91,15 @@ trait CardDAV $this->oLogger->Write($sCmd.' '.$sUrl.(('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData ? ' ('.\strlen($mData).')' : ''), \LOG_INFO, 'DAV'); -// if ('PUT' === $sCmd || 'POST' === $sCmd) -// { -// $this->oLogger->Write($mData, \LOG_INFO, 'DAV'); -// } - try { - if (('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData) - { + if (('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData) { return $oClient->request($sCmd, $sUrl, $mData, array( 'Content-Type' => 'text/vcard; charset=utf-8' )); } - else - { - return $oClient->request($sCmd, $sUrl); - } - -// if ('GET' === $sCmd) -// { + return $oClient->request($sCmd, $sUrl); +// if ('GET' === $sCmd) { // $this->oLogger->WriteDump($aResponse, \LOG_INFO, 'DAV'); // } } @@ -131,11 +113,9 @@ trait CardDAV private function detectionPropFind(DAVClient $oClient, string $sPath) : ?array { - $aResponse = null; - try { - $aResponse = $oClient->propFind($sPath, array( + return $oClient->propFind($sPath, array( '{DAV:}current-user-principal', '{DAV:}resourcetype', '{DAV:}displayname', @@ -147,7 +127,7 @@ trait CardDAV $this->oLogger->WriteException($oException); } - return $aResponse; + return null; } private function getContactsPaths(DAVClient $oClient, string $sPath, string $sUser, string $sPassword, string $sProxy = '') : array @@ -162,10 +142,8 @@ trait CardDAV $sNextPath = ''; $sFirstNextPath = ''; - if (\is_array($aResponse)) - { - foreach ($aResponse as $sPropPath => $aItem) - { + if (\is_array($aResponse)) { + foreach ($aResponse as $sPropPath => $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')) { @@ -173,23 +151,18 @@ trait CardDAV continue; } - if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) - { + if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) { $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']['{DAV:}href']; continue; } - if (!empty($sPropPath)) - { - if (empty($sFirstNextPath)) - { + if (!empty($sPropPath)) { + if (empty($sFirstNextPath)) { $sFirstNextPath = $sPropPath; } - if (empty($sNextPath)) - { - if (static::hasDAVCollection($aItem)) - { + if (empty($sNextPath)) { + if (static::hasDAVCollection($aItem)) { $sNextPath = $sPropPath; continue; } @@ -197,23 +170,18 @@ trait CardDAV } } - if (empty($sNextPath) && empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet) && !empty($sFirstNextPath)) - { + if (empty($sNextPath) && empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet) && !empty($sFirstNextPath)) { $sNextPath = $sFirstNextPath; } } - if (empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet)) - { - if (empty($sNextPath)) - { + if (empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet)) { + if (empty($sNextPath)) { return $aContactsPaths; } - if (\preg_match('/^http[s]?:\/\//i', $sNextPath)) - { + if (\preg_match('/^http[s]?:\/\//i', $sNextPath)) { $oClient = $this->getDavClientFromUrl($sNextPath, $sUser, $sPassword, $sProxy); - if (!$oClient) - { + if (!$oClient) { return $aContactsPaths; } $sNextPath = $oClient->__UrlPath__; @@ -221,10 +189,8 @@ trait CardDAV if ($sPath != $sNextPath) { $aResponse = $this->detectionPropFind($oClient, $sNextPath); - if (\is_array($aResponse)) - { - foreach ($aResponse as $aItem) - { + 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')) { @@ -232,8 +198,7 @@ trait CardDAV continue; } - if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) - { + if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']['{DAV:}href'])) { $sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal']['{DAV:}href']; continue; } @@ -242,30 +207,21 @@ trait CardDAV } } - if (empty($sAddressbookHomeSet)) - { - if (empty($sCurrentUserPrincipal)) - { + if (empty($sAddressbookHomeSet)) { + if (empty($sCurrentUserPrincipal)) { return $aContactsPaths; } - if (\preg_match('/^http[s]?:\/\//i', $sCurrentUserPrincipal)) - { + if (\preg_match('/^http[s]?:\/\//i', $sCurrentUserPrincipal)) { $oClient = $this->getDavClientFromUrl($sCurrentUserPrincipal, $sUser, $sPassword, $sProxy); - if ($oClient) - { - $sCurrentUserPrincipal = $oClient->__UrlPath__; - } - else - { + if (!$oClient) { return $aContactsPaths; } + $sCurrentUserPrincipal = $oClient->__UrlPath__; } $aResponse = $this->detectionPropFind($oClient, $sCurrentUserPrincipal); - if (\is_array($aResponse)) - { - foreach ($aResponse as $aItem) - { + 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')) { @@ -276,28 +232,20 @@ trait CardDAV } } - if (empty($sAddressbookHomeSet)) - { + if (empty($sAddressbookHomeSet)) { return $aContactsPaths; } - if (\preg_match('/^http[s]?:\/\//i', $sAddressbookHomeSet)) - { + if (\preg_match('/^http[s]?:\/\//i', $sAddressbookHomeSet)) { $oClient = $this->getDavClientFromUrl($sAddressbookHomeSet, $sUser, $sPassword, $sProxy); - if ($oClient) - { - $sAddressbookHomeSet = $oClient->__UrlPath__; - } - else - { + if (!$oClient) { return $aContactsPaths; } + $sAddressbookHomeSet = $oClient->__UrlPath__; } $aResponse = $this->detectionPropFind($oClient, $sAddressbookHomeSet); - if (\is_array($aResponse)) - { - foreach ($aResponse as $sPropPath => $aItem) - { + 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'])) { @@ -314,11 +262,6 @@ trait CardDAV */ private function checkContactsPath(DAVClient $oClient, string $sPath) : bool { - if (!$oClient) - { - return false; - } - $aResponse = null; try { @@ -332,21 +275,17 @@ trait CardDAV } $bGood = false; - if (\is_array($aResponse)) - { - foreach ($aResponse as $sKey => $aItem) - { + if (\is_array($aResponse)) { + foreach ($aResponse as $sKey => $aItem) { if (!empty($sKey) && static::hasDAVCollection($aItem) && \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype'])) { $bGood = true; } } - } - - if ($bGood) - { - $oClient->__UrlPath__ = $sPath; + if ($bGood) { + $oClient->__UrlPath__ = $sPath; + } } return $bGood; @@ -354,8 +293,7 @@ trait CardDAV private function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : DAVClient { - if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) - { + if (!\preg_match('/^http[s]?:\/\//i', $sUrl)) { $sUrl = \preg_replace('/^fruux\.com/i', 'dav.fruux.com', $sUrl); $sUrl = \preg_replace('/^icloud\.com/i', 'contacts.icloud.com', $sUrl); $sUrl = \preg_replace('/^gmail\.com/i', 'google.com', $sUrl); @@ -363,8 +301,7 @@ trait CardDAV } $aUrl = \parse_url($sUrl); - if (!\is_array($aUrl)) - { + if (!\is_array($aUrl)) { $aUrl = array(); } @@ -381,8 +318,7 @@ trait CardDAV $this->oLogger->AddSecret($sPassword); - if (!empty($sProxy)) - { + if (!empty($sProxy)) { $aSettings['proxy'] = $sProxy; } @@ -409,8 +345,7 @@ trait CardDAV $aMatch = array(); $sUserAddressBookNameName = ''; - if (\preg_match('/\|(.+)$/', $sUrl, $aMatch) && !empty($aMatch[1])) - { + if (\preg_match('/\|(.+)$/', $sUrl, $aMatch) && !empty($aMatch[1])) { $sUserAddressBookNameName = \trim($aMatch[1]); $sUserAddressBookNameName = \mb_strtolower($sUserAddressBookNameName); @@ -422,8 +357,7 @@ trait CardDAV $sPath = $oClient->__UrlPath__; $bGood = true; - if ('' === $sPath || '/' === $sPath || !$this->checkContactsPath($oClient, $sPath)) - { + if ('' === $sPath || '/' === $sPath || !$this->checkContactsPath($oClient, $sPath)) { /** * Path is not an addressbook, try to find it */ @@ -431,30 +365,22 @@ trait CardDAV $this->oLogger->WriteDump($aPaths); $sNewPath = ''; - if (\is_array($aPaths)) - { - if (1 < \count($aPaths)) - { - if ('' !== $sUserAddressBookNameName) - { - foreach ($aPaths as $sKey => $sValue) - { + if (\is_array($aPaths)) { + if (1 < \count($aPaths)) { + if ('' !== $sUserAddressBookNameName) { + foreach ($aPaths as $sKey => $sValue) { $sValue = \mb_strtolower(\trim($sValue)); - if ($sValue === $sUserAddressBookNameName) - { + if ($sValue === $sUserAddressBookNameName) { $sNewPath = $sKey; break; } } } - if (empty($sNewPath)) - { - foreach ($aPaths as $sKey => $sValue) - { + if (empty($sNewPath)) { + foreach ($aPaths as $sKey => $sValue) { $sValue = \mb_strtolower($sValue); - if (\in_array($sValue, array('contacts', 'default', 'addressbook', 'address book'))) - { + if (\in_array($sValue, array('contacts', 'default', 'addressbook', 'address book'))) { $sNewPath = $sKey; break; } @@ -462,19 +388,18 @@ trait CardDAV } } - if (empty($sNewPath)) - { - foreach ($aPaths as $sKey => $sValue) - { + if (empty($sNewPath)) { + foreach ($aPaths as $sKey => $sValue) { $sNewPath = $sKey; break; } } } - $sPath = $sNewPath; - - $bGood = $sPath && $this->checkContactsPath($oClient, $sPath); + $bGood = $sNewPath && $this->checkContactsPath($oClient, $sNewPath); + if (!$bGood) { + $this->oLogger->Write('Contacts path not found at: '.$sPath, \LOG_INFO, 'DAV'); + } } return $bGood ? $oClient : null; From 56c681bbd54294717c00cf7f75990347fa85f377 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 15:25:24 +0100 Subject: [PATCH 0396/1012] https://github.com/the-djmaze/snappymail/issues/724#issuecomment-1369826672 --- snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index 3053aaa82..dcca293d0 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -665,8 +665,8 @@ class MailClient $oMessageCollection->FolderInfo = $oInfo; $oMessageCollection->totalEmails = $oInfo->MESSAGES; - $bUseThreads = $oParams->bUseThreads - && ($this->oImapClient->hasCapability('THREAD=REFS') || $this->oImapClient->hasCapability('THREAD=REFERENCES') || $this->oImapClient->hasCapability('THREAD=ORDEREDSUBJECT')); + $bUseThreads = $oParams->bUseThreads && $this->oImapClient->CapabilityValue('THREAD'); +// && ($this->oImapClient->hasCapability('THREAD=REFS') || $this->oImapClient->hasCapability('THREAD=REFERENCES') || $this->oImapClient->hasCapability('THREAD=ORDEREDSUBJECT')); if ($oParams->iThreadUid && !$bUseThreads) { throw new \InvalidArgumentException('THREAD not supported'); } @@ -686,6 +686,8 @@ class MailClient $message_list_limit = $this->oImapClient->Settings->message_list_limit; if (0 < $message_list_limit && $message_list_limit < $oInfo->MESSAGES) { +// if ((0 < $message_list_limit && $message_list_limit < $oInfo->MESSAGES) +// || (!$this->oImapClient->hasCapability('SORT') && !$this->oImapClient->CapabilityValue('THREAD'))) { // Don't use THREAD for speed $oMessageCollection->Limited = true; if ($this->oLogger) { From 1a71309b8188750212cea7f78ca0cac3d641a07d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 15:46:26 +0100 Subject: [PATCH 0397/1012] Drop confusing oldpass from default change-password query --- plugins/change-password/drivers/pdo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/change-password/drivers/pdo.php b/plugins/change-password/drivers/pdo.php index 80fbc57dc..06a601f46 100644 --- a/plugins/change-password/drivers/pdo.php +++ b/plugins/change-password/drivers/pdo.php @@ -38,7 +38,7 @@ class ChangePasswordDriverPDO \RainLoop\Plugins\Property::NewInstance('pdo_sql')->SetLabel('Statement') ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) ->SetDescription('SQL statement (allowed wildcards :email, :oldpass, :newpass, :domain, :username, :login_name).') - ->SetDefaultValue('UPDATE table SET password = :newpass WHERE domain = :domain AND username = :username and oldpass = :oldpass'), + ->SetDefaultValue('UPDATE table SET password = :newpass WHERE domain = :domain AND username = :username'), \RainLoop\Plugins\Property::NewInstance('pdo_encrypt')->SetLabel('Encryption') ->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECTION) ->SetDefaultValue(array('none', 'bcrypt', 'Argon2i', 'Argon2id', 'SHA256-CRYPT', 'SHA512-CRYPT')) From e9504baa121ada7416f992668cefd9ee2d4a60c6 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 16:11:20 +0100 Subject: [PATCH 0398/1012] Bugfix: Typed property MailSo\Mail\MessageListParams::$sSearch must not be accessed before initialization --- .../v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php index 27dc8a068..5a6ce68a3 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php @@ -15,7 +15,7 @@ class MessageListParams { public string $sFolderName, - $sSearch, + $sSearch = '', $sSort = ''; public ?\MailSo\Cache\CacheClient From 5ff8b28a12bb08359521ec2c751bd7526ff7859a Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 16:22:57 +0100 Subject: [PATCH 0399/1012] Bugfix: Kolab AddressBook failed due to past changes --- plugins/kolab/KolabAddressBook.php | 4 ++-- plugins/kolab/index.php | 2 +- .../v/0.0.0/app/libraries/MailSo/Mail/Message.php | 10 ++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/kolab/KolabAddressBook.php b/plugins/kolab/KolabAddressBook.php index 7ade62c11..475325ac9 100644 --- a/plugins/kolab/KolabAddressBook.php +++ b/plugins/kolab/KolabAddressBook.php @@ -58,12 +58,12 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt $xCard = null; try { foreach ($oMessage->Attachments() ?: [] as $oAttachment) { - if ('application/vcard+xml' === $oAttachment->MimeType()) { + if ('application/vcard+xml' === $oAttachment->ContentType()) { $result = $this->MailClient()->MessageMimeStream(function ($rResource) use (&$xCard) { if (\is_resource($rResource)) { $xCard = \Sabre\VObject\Reader::readXML($rResource); } - }, $this->sFolderName, $oMessage->Uid(), $oAttachment->MimeIndex()); + }, $this->sFolderName, $oMessage->Uid(), $oAttachment->PartID()); break; } } diff --git a/plugins/kolab/index.php b/plugins/kolab/index.php index be93fb382..46581ee54 100644 --- a/plugins/kolab/index.php +++ b/plugins/kolab/index.php @@ -8,7 +8,7 @@ class KolabPlugin extends \RainLoop\Plugins\AbstractPlugin RELEASE = '2023-01-03', CATEGORY = 'Contacts', DESCRIPTION = 'Use an Address Book of Kolab.', - REQUIRED = '2.24.4'; + REQUIRED = '2.24.5'; public function Init() : void { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php index 2efde4ad3..70e7c740b 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php @@ -107,6 +107,16 @@ class Message implements \JsonSerializable return $this->Uid; } + public function HeaderTimeStampInUTC() : int + { + return $this->HeaderTimeStampInUTC; + } + + public function Attachments() : ?AttachmentCollection + { + return $this->Attachments; + } + public function Plain() : string { return $this->sPlain; From a9c60137da0e04e6f5d3fdf52fdebeaa25cb00cd Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 17:00:58 +0100 Subject: [PATCH 0400/1012] Added untested KolabAddressBook autocreate contacts #812 --- plugins/kolab/KolabAddressBook.php | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/plugins/kolab/KolabAddressBook.php b/plugins/kolab/KolabAddressBook.php index 475325ac9..1876ee1d0 100644 --- a/plugins/kolab/KolabAddressBook.php +++ b/plugins/kolab/KolabAddressBook.php @@ -1,6 +1,7 @@ $sAddress) { + $sSearch = \MailSo\Imap\SearchCriterias::escapeSearchString($this->ImapClient(), $sEmail); + if (!$this->ImapClient()->MessageSimpleSearch("FROM {$sSearch}")) { + $oVCard = new VCard; + $oVCard->add('EMAIL', $sEmail); + $sFullName = \trim(\MailSo\Mime\Email::Parse(\trim($sAddress))->GetDisplayName()); + if ('' !== $sFullName) { + $sFirst = $sLast = ''; + if (false !== \strpos($sFullName, ' ')) { + $aNames = \explode(' ', $sFullName, 2); + $sFirst = isset($aNames[0]) ? $aNames[0] : ''; + $sLast = isset($aNames[1]) ? $aNames[1] : ''; + } else { + $sFirst = $sFullName; + } + if (\strlen($sFirst) || \strlen($sLast)) { + $oVCard->N = array($sLast, $sFirst, '', '', ''); + } + } + $oContact = new Contact(); + $oContact->setVCard($oVCard); + $this->ContactSave($oContact); + } + } + return true; + } return false; } From 02a553c79feb2722d8e947289da3f018d1d246c9 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 3 Jan 2023 20:56:38 +0100 Subject: [PATCH 0401/1012] Resolve #826 --- dev/External/SquireUI.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dev/External/SquireUI.js b/dev/External/SquireUI.js index e7f6e5343..04decb19b 100644 --- a/dev/External/SquireUI.js +++ b/dev/External/SquireUI.js @@ -14,7 +14,6 @@ const createElement = name => doc.createElement(name), tpl = createElement('template'), - clr = createElement('input'), trimLines = html => html.trim().replace(/^(
    \s*\s*<\/div>)+/, '').trim(), htmlToPlain = html => rl.Utils.htmlToPlain(html).trim(), @@ -61,18 +60,21 @@ const } }; -clr.type = "color"; -clr.style.display = 'none'; -doc.body.append(clr); - class SquireUI { constructor(container) { const - doClr = name => () => { + clr = createElement('input'), + doClr = name => input => { + // https://github.com/the-djmaze/snappymail/issues/826 + var rect = input.getBoundingClientRect(); + clr.style.top = (rect.bottom - clr.offsetHeight) + 'px'; + clr.style.left = rect.left + 'px'; + clr.style.width = (rect.right - rect.left) + 'px'; + clr.value = ''; clr.onchange = () => squire.setStyle({[name]:clr.value}); - clr.click(); + setTimeout(()=>clr.click(),1); }, actions = { @@ -258,6 +260,11 @@ class SquireUI browseImage = createElement('input'), squire = new Squire(wysiwyg, SquireDefaultConfig); + clr.type = "color"; + clr.style.opacity = 0; + clr.style.position = 'fixed'; + container.append(clr); + browseImage.type = 'file'; browseImage.accept = 'image/*'; browseImage.style.display = 'none'; From e95b919c1e3c443f995d92d0d2c65154b0d301c8 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 4 Jan 2023 16:11:10 +0100 Subject: [PATCH 0402/1012] Resolve #825 --- plugins/nextcloud/index.php | 4 +- plugins/nextcloud/js/message.js | 1 + plugins/nextcloud/js/webdav.js | 87 +++++++++++-------- plugins/nextcloud/style.css | 1 + .../templates/PopupsNextcloudFiles.html | 5 ++ 5 files changed, 59 insertions(+), 39 deletions(-) diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php index 71f23ab63..e91d91ef1 100644 --- a/plugins/nextcloud/index.php +++ b/plugins/nextcloud/index.php @@ -4,8 +4,8 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Nextcloud', - VERSION = '2.13', - RELEASE = '2022-12-16', + VERSION = '2.14', + RELEASE = '2023-01-04', CATEGORY = 'Integrations', DESCRIPTION = 'Integrate with Nextcloud v20+', REQUIRED = '2.23.0'; diff --git a/plugins/nextcloud/js/message.js b/plugins/nextcloud/js/message.js index 37f2956df..922fa8781 100644 --- a/plugins/nextcloud/js/message.js +++ b/plugins/nextcloud/js/message.js @@ -120,6 +120,7 @@ view.message.subscribe(msg => { view.nextcloudICS(null); if (msg && cfg.CalDAV) { +// let ics = msg.attachments.find(attachment => 'application/ics' == attachment.mimeType); let ics = msg.attachments.find(attachment => 'text/calendar' == attachment.mimeType); if (ics && ics.download) { // fetch it and parse the VEVENT diff --git a/plugins/nextcloud/js/webdav.js b/plugins/nextcloud/js/webdav.js index 9915dd2b8..d5c77da61 100644 --- a/plugins/nextcloud/js/webdav.js +++ b/plugins/nextcloud/js/webdav.js @@ -149,31 +149,14 @@ const items.forEach(item => { if (item.isFile) { let li = document.createElement('li'), - btn = document.createElement('button'); + cb = document.createElement('input'); li.item = item; li.textContent = item.name.replace(/^.*\/([^/]+)$/, '$1'); li.dataset.icon = '🗎'; - btn.name = 'select'; - btn.dataset.icon = '📎'; - btn.textContent = 'attach'; - btn.className = 'button-vue'; - li.append(btn); - - btn = document.createElement('button'); - btn.name = 'share-internal'; - btn.dataset.icon = '🔗'; - btn.textContent = 'internal'; - btn.className = 'button-vue'; - li.append(btn); - - btn = document.createElement('button'); - btn.name = 'share-public'; - btn.dataset.icon = '🔗'; - btn.textContent = 'public'; - btn.className = 'button-vue'; - li.append(btn); + cb.type = 'checkbox'; + li.append(cb); parent.append(li); } @@ -203,28 +186,42 @@ class NextcloudFilesPopupView extends rl.pluginPopupView { }); } - onBuild(dom) { - this.tree = dom.querySelector('#sm-nc-files-tree'); - this.tree.addEventListener('click', event => { - let el = event.target; - if (el.matches('button')) { - let parent = el.parentNode, - item = parent.item; - if ('select' == el.name) { - this.select = this.files() ? [item] : parent.item_name; + attach() { + this.select = []; + this.tree.querySelectorAll('input').forEach(input => + input.checked && this.select.push(input.parentNode.item) + ); + this.close(); + } + + shareInternal() { + this.select = []; + this.tree.querySelectorAll('input').forEach(input => + input.checked && this.select.push({url:generateRemoteUrl(`/f/${input.parentNode.item.id}`)}) + ); + this.close(); + } + + sharePublic() { + const inputs = [...this.tree.querySelectorAll('input')], + loop = () => { + if (!inputs.length) { this.close(); - } else if ('share-internal' == el.name) { - this.select = [{url:generateRemoteUrl(`/f/${item.id}`)}]; - this.close(); - } else if ('share-public' == el.name) { + return; + } + const input = inputs.pop(); + if (!input.checked) { + loop(); + } else { + const item = input.parentNode.item; if (item.shared) { ncFetch( shareUrl() + `?format=json&path=${encodeURIComponent(item.name)}&reshares=true` ) .then(response => (response.status < 400) ? response.json() : Promise.reject(new Error({ response }))) .then(json => { - this.select = [{url:json.ocs.data[0].url}]; - this.close(); + this.select.push({url:json.ocs.data[0].url}); + loop(); // json.data[0].password }); } else { @@ -246,10 +243,26 @@ class NextcloudFilesPopupView extends rl.pluginPopupView { .then(response => (response.status < 400) ? response.json() : Promise.reject(new Error({ response }))) .then(json => { // PUT /ocs/v2.php/apps/files_sharing/api/v1/shares/2 {"password":"ABC09"} - this.select = [{url:json.ocs.data.url}]; - this.close(); + this.select.push({url:json.ocs.data.url}); + loop(); }); } + } + }; + + this.select = []; + loop(); + } + + onBuild(dom) { + this.tree = dom.querySelector('#sm-nc-files-tree'); + this.tree.addEventListener('click', event => { + let el = event.target; + if (el.matches('button')) { + let parent = el.parentNode; + if ('select' == el.name) { + this.select = parent.item_name; + this.close(); } else if ('create' == el.name) { let name = el.input.value.replace(/[|\\?*<":>+[]\/&\s]/g, ''); if (name.length) { diff --git a/plugins/nextcloud/style.css b/plugins/nextcloud/style.css index e99ac0767..4dd142992 100644 --- a/plugins/nextcloud/style.css +++ b/plugins/nextcloud/style.css @@ -5,6 +5,7 @@ clear: right; line-height: 2.5em; } +#V-PopupsNextcloudFiles li input, #V-PopupsNextcloudFiles li button { cursor: pointer; float: right; diff --git a/plugins/nextcloud/templates/PopupsNextcloudFiles.html b/plugins/nextcloud/templates/PopupsNextcloudFiles.html index f63656034..1be321dae 100644 --- a/plugins/nextcloud/templates/PopupsNextcloudFiles.html +++ b/plugins/nextcloud/templates/PopupsNextcloudFiles.html @@ -11,3 +11,8 @@ -->
      +
      + + + +
      From d6d9be0dab3d54ccf06e43a9288e7865927da376 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 4 Jan 2023 18:46:04 +0100 Subject: [PATCH 0403/1012] Improve #826 --- dev/External/SquireUI.js | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/External/SquireUI.js b/dev/External/SquireUI.js index 04decb19b..e8fa93ef1 100644 --- a/dev/External/SquireUI.js +++ b/dev/External/SquireUI.js @@ -263,6 +263,7 @@ class SquireUI clr.type = "color"; clr.style.opacity = 0; clr.style.position = 'fixed'; + clr.style.left = '-100em'; container.append(clr); browseImage.type = 'file'; From d4e1e90eef3dcaf743fe947d2353181750e2e02e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 5 Jan 2023 09:55:09 +0100 Subject: [PATCH 0404/1012] Better solution for #826 --- dev/External/SquireUI.js | 13 ++++--------- dev/Styles/User/SquireUI.less | 9 +++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dev/External/SquireUI.js b/dev/External/SquireUI.js index e8fa93ef1..4a1c87ef5 100644 --- a/dev/External/SquireUI.js +++ b/dev/External/SquireUI.js @@ -67,10 +67,8 @@ class SquireUI clr = createElement('input'), doClr = name => input => { // https://github.com/the-djmaze/snappymail/issues/826 - var rect = input.getBoundingClientRect(); - clr.style.top = (rect.bottom - clr.offsetHeight) + 'px'; - clr.style.left = rect.left + 'px'; - clr.style.width = (rect.right - rect.left) + 'px'; + clr.style.left = (input.offsetLeft + input.parentNode.offsetLeft) + 'px'; + clr.style.width = input.offsetWidth + 'px'; clr.value = ''; clr.onchange = () => squire.setStyle({[name]:clr.value}); @@ -260,11 +258,8 @@ class SquireUI browseImage = createElement('input'), squire = new Squire(wysiwyg, SquireDefaultConfig); - clr.type = "color"; - clr.style.opacity = 0; - clr.style.position = 'fixed'; - clr.style.left = '-100em'; - container.append(clr); + clr.type = 'color'; + toolbar.append(clr); browseImage.type = 'file'; browseImage.accept = 'image/*'; diff --git a/dev/Styles/User/SquireUI.less b/dev/Styles/User/SquireUI.less index e7fe24dc5..5f3c762f8 100644 --- a/dev/Styles/User/SquireUI.less +++ b/dev/Styles/User/SquireUI.less @@ -5,9 +5,18 @@ overflow-x: auto; overflow-y: hidden; padding: 2px; + position: relative; white-space: nowrap; } +.squire-toolbar input[type="color"] { + bottom: 0; + left: -100em; + opacity: 0; + position: absolute; + width: 40px; +} + .squire-toolbar select { font-size: 12px; padding: 4px 1.5em 4px 6px; From a2ddd493cee90878cb791e1ee566f2e8026c1811 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 5 Jan 2023 10:55:59 +0100 Subject: [PATCH 0405/1012] Improved MIME handling for JSON --- .../libraries/MailSo/Imap/BodyStructure.php | 191 +++++++----------- .../app/libraries/MailSo/Mail/Attachment.php | 1 + .../app/libraries/MailSo/Mail/Message.php | 4 +- .../app/libraries/MailSo/Mime/Attachment.php | 8 +- .../0.0.0/app/libraries/MailSo/Mime/Email.php | 5 + .../libraries/MailSo/Mime/EmailCollection.php | 5 + .../app/libraries/MailSo/Mime/Header.php | 12 +- .../app/libraries/MailSo/Mime/Message.php | 7 +- .../app/libraries/MailSo/Mime/Parameter.php | 16 +- .../MailSo/Mime/ParameterCollection.php | 21 +- .../0.0.0/app/libraries/MailSo/Mime/Part.php | 7 +- 11 files changed, 134 insertions(+), 143 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php index 11c77e330..070a27521 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php @@ -11,6 +11,8 @@ namespace MailSo\Imap; +use MailSo\Mime\ParameterCollection; + /** * @category MailSo * @package Imap @@ -19,9 +21,9 @@ class BodyStructure { private string $sContentType; - private string $sCharset; + private array $aContentTypeParams; - private array $aBodyParams; + private string $sCharset; private string $sContentID; @@ -63,6 +65,11 @@ class BodyStructure return $this->sContentType; } + public function ContentTypeParameters() : array + { + return $this->aContentTypeParams; + } + public function Size() : int { return $this->iSize; @@ -133,8 +140,8 @@ class BodyStructure { // https://datatracker.ietf.org/doc/html/rfc3156#section-4 return 'multipart/encrypted' === $this->sContentType - && !empty($this->aBodyParams['protocol']) - && 'application/pgp-encrypted' === \strtolower(\trim($this->aBodyParams['protocol'])) + && !empty($this->aContentTypeParams['protocol']) + && 'application/pgp-encrypted' === \strtolower(\trim($this->aContentTypeParams['protocol'])) // The multipart/encrypted body MUST consist of exactly two parts. && 2 === \count($this->aSubParts) && 'application/pgp-encrypted' === $this->aSubParts[0]->ContentType() @@ -146,8 +153,8 @@ class BodyStructure { // https://datatracker.ietf.org/doc/html/rfc3156#section-5 return 'multipart/signed' === $this->sContentType - && !empty($this->aBodyParams['protocol']) - && 'application/pgp-signature' === \strtolower(\trim($this->aBodyParams['protocol'])) + && !empty($this->aContentTypeParams['protocol']) + && 'application/pgp-signature' === \strtolower(\trim($this->aContentTypeParams['protocol'])) // The multipart/signed body MUST consist of exactly two parts. && 2 === \count($this->aSubParts) && $this->aSubParts[1]->IsPgpSignature(); @@ -172,8 +179,8 @@ class BodyStructure public function IsFlowedFormat() : bool { - return !empty($this->aBodyParams['format']) - && 'flowed' === \strtolower(\trim($this->aBodyParams['format'])) + return !empty($this->aContentTypeParams['format']) + && 'flowed' === \strtolower(\trim($this->aContentTypeParams['format'])) && !\in_array($this->sMailEncodingName, array('base64', 'quoted-printable')); } @@ -255,20 +262,15 @@ class BodyStructure public function GetPartByMimeIndex(string $sMimeIndex) : self { $oPart = null; - if (\strlen($sMimeIndex)) - { - if ($sMimeIndex === $this->sPartID) - { + if (\strlen($sMimeIndex)) { + if ($sMimeIndex === $this->sPartID) { $oPart = $this; } - if (null === $oPart) - { - foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) - { + if (null === $oPart) { + foreach ($this->aSubParts as /* @var $oSubPart \MailSo\Imap\BodyStructure */ $oSubPart) { $oPart = $oSubPart->GetPartByMimeIndex($sMimeIndex); - if (null !== $oPart) - { + if (null !== $oPart) { break; } } @@ -281,60 +283,43 @@ class BodyStructure private static function decodeAttrParameter(array $aParams, string $sParamName, string $sCharset) : string { $sResult = ''; - if (isset($aParams[$sParamName])) - { + if (isset($aParams[$sParamName])) { $sResult = \MailSo\Base\Utils::DecodeHeaderValue($aParams[$sParamName], $sCharset); - } - else if (isset($aParams[$sParamName.'*'])) - { + } else if (isset($aParams[$sParamName.'*'])) { $aValueParts = \explode("''", $aParams[$sParamName.'*'], 2); - if (2 === \count($aValueParts)) - { + if (2 === \count($aValueParts)) { $sCharset = isset($aValueParts[0]) ? $aValueParts[0] : \MailSo\Base\Enumerations\Charset::UTF_8; - $sResult = \MailSo\Base\Utils::ConvertEncoding( \urldecode($aValueParts[1]), $sCharset, \MailSo\Base\Enumerations\Charset::UTF_8); - } - else - { + } else { $sResult = \urldecode($aParams[$sParamName.'*']); } - } - else - { + } else { $sCharset = ''; $sCharsetIndex = -1; $aFileNames = array(); - foreach ($aParams as $sName => $sValue) - { + foreach ($aParams as $sName => $sValue) { $aMatches = array(); - if (\preg_match('/^'.\preg_quote($sParamName, '/').'\*([0-9]+)\*$/i', $sName, $aMatches)) - { + if (\preg_match('/^'.\preg_quote($sParamName, '/').'\*([0-9]+)\*$/i', $sName, $aMatches)) { $iIndex = (int) $aMatches[1]; - if ($sCharsetIndex < $iIndex && false !== \strpos($sValue, "''")) - { + if ($sCharsetIndex < $iIndex && false !== \strpos($sValue, "''")) { $aValueParts = \explode("''", $sValue, 2); - if (2 === \count($aValueParts) && \strlen($aValueParts[0])) - { + if (2 === \count($aValueParts) && \strlen($aValueParts[0])) { $sCharsetIndex = $iIndex; $sCharset = $aValueParts[0]; $sValue = $aValueParts[1]; } } - $aFileNames[$iIndex] = $sValue; } } - if (\count($aFileNames)) - { + if (\count($aFileNames)) { \ksort($aFileNames, SORT_NUMERIC); $sResult = \implode(\array_values($aFileNames)); $sResult = \urldecode($sResult); - - if (\strlen($sCharset)) - { + if (\strlen($sCharset)) { $sResult = \MailSo\Base\Utils::ConvertEncoding($sResult, $sCharset, \MailSo\Base\Enumerations\Charset::UTF_8); } @@ -353,8 +338,8 @@ class BodyStructure $sContentTypeMain = ''; $sContentTypeSub = ''; $aSubParts = array(); - $aBodyParams = array(); - $sName = ''; + $aContentTypeParams = array(); + $sFileName = ''; $sCharset = ''; // \MailSo\Base\Enumerations\Charset::UTF_8 ? $sContentID = ''; $sDescription = ''; @@ -362,20 +347,16 @@ class BodyStructure $iSize = 0; $iExtraItemPos = 0; // list index of items which have no well-established position (such as 0, 1, 5, etc). - if (\is_array($aBodyStructure[0])) - { + if (\is_array($aBodyStructure[0])) { // Process multipart body structure $sContentTypeMain = 'multipart'; $sContentTypeSub = 'mixed'; // primary default $sSubPartIDPrefix = ''; - if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) - { + if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { // This multi-part is root part of message. $sSubPartIDPrefix = $sPartID; $sPartID .= 'TEXT'; - } - else if (\strlen($sPartID)) - { + } else if (\strlen($sPartID)) { // This multi-part is a part of another multi-part. $sSubPartIDPrefix = $sPartID.'.'; } @@ -387,18 +368,15 @@ class BodyStructure ("text" "plain" ("charset" "utf-8") …) ("text" "html" …) */ - while ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) - { + while ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { $oPart = self::NewInstance($aBodyStructure[$iExtraItemPos], $sSubPartIDPrefix.$iIndex); - if (!$oPart) - { + if (!$oPart) { return null; } // For multipart, we have no charset info in the part itself. Thus, // obtain charset from nested parts. - if (!$sCharset) - { + if (!$sCharset) { $sCharset = $oPart->Charset(); } @@ -411,26 +389,20 @@ class BodyStructure * Now process the subparts containter like: "alternative" ("boundary" "--boundary_id") … */ - if ($iExtraItemPos < \count($aBodyStructure)) - { - if (!\is_string($aBodyStructure[$iExtraItemPos])) - { + if ($iExtraItemPos < \count($aBodyStructure)) { + if (!\is_string($aBodyStructure[$iExtraItemPos])) { return null; } $sContentTypeSub = \strtolower($aBodyStructure[$iExtraItemPos]); ++$iExtraItemPos; - if ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) - { - $aBodyParams = self::getKeyValueListFromArrayList($aBodyStructure[$iExtraItemPos]); + if ($iExtraItemPos < \count($aBodyStructure) && \is_array($aBodyStructure[$iExtraItemPos])) { + $aContentTypeParams = self::getKeyValueListFromArrayList($aBodyStructure[$iExtraItemPos]); } } - } - else if (\is_string($aBodyStructure[0])) - { + } else if (\is_string($aBodyStructure[0])) { // Process simple (singlepart) body structure - if (7 > \count($aBodyStructure) || !\is_string($aBodyStructure[1])) - { + if (7 > \count($aBodyStructure) || !\is_string($aBodyStructure[1])) { return null; } @@ -438,39 +410,33 @@ class BodyStructure $sContentTypeSub = \strtolower($aBodyStructure[1]); $aBodyParamList = $aBodyStructure[2]; - if (\is_array($aBodyParamList)) - { - $aBodyParams = self::getKeyValueListFromArrayList($aBodyParamList); - if (isset($aBodyParams['charset'])) - { - $sCharset = $aBodyParams['charset']; + if (\is_array($aBodyParamList)) { + $aContentTypeParams = self::getKeyValueListFromArrayList($aBodyParamList); + if (isset($aContentTypeParams['charset'])) { + $sCharset = $aContentTypeParams['charset']; + } + $sFileName = self::decodeAttrParameter($aContentTypeParams, 'name', $sCharset); + if ($sFileName) { + $aContentTypeParams['name'] = $sFileName; } - - $sName = self::decodeAttrParameter($aBodyParams, 'name', $sCharset); } - if (null !== $aBodyStructure[3]) - { - if (!\is_string($aBodyStructure[3])) - { + if (null !== $aBodyStructure[3]) { + if (!\is_string($aBodyStructure[3])) { return null; } $sContentID = $aBodyStructure[3]; } - if (null !== $aBodyStructure[4]) - { - if (!\is_string($aBodyStructure[4])) - { + if (null !== $aBodyStructure[4]) { + if (!\is_string($aBodyStructure[4])) { return null; } $sDescription = $aBodyStructure[4]; } - if (null !== $aBodyStructure[5]) - { - if (!\is_string($aBodyStructure[5])) - { + if (null !== $aBodyStructure[5]) { + if (!\is_string($aBodyStructure[5])) { return null; } $sMailEncodingName = $aBodyStructure[5]; @@ -478,24 +444,20 @@ class BodyStructure $iSize = \is_numeric($aBodyStructure[6]) ? (int) $aBodyStructure[6] : -1; - if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) - { + if (!\strlen($sPartID) || '.' === $sPartID[\strlen($sPartID) - 1]) { // This is the only sub-part of the message (otherwise, it would be // one of sub-parts of a multi-part, and partID would already be fully set up). $sPartID .= '1'; } $iExtraItemPos = 7; - if ('text' === $sContentTypeMain) - { + if ('text' === $sContentTypeMain) { /** * A body type of type TEXT contains, immediately after the basic * fields, the size of the body in text lines. */ ++$iExtraItemPos; - } - else if ('message' === $sContentTypeMain && 'rfc822' === $sContentTypeSub) - { + } else if ('message' === $sContentTypeMain && 'rfc822' === $sContentTypeSub) { /** * A body type of type MESSAGE and subtype RFC822 contains, * immediately after the basic fields, the envelope structure, @@ -503,9 +465,7 @@ class BodyStructure */ $iExtraItemPos += 3; } - } - else - { + } else { return null; } @@ -515,18 +475,14 @@ class BodyStructure $sDisposition = ''; $sFileName = ''; - if ($iExtraItemPos < \count($aBodyStructure)) - { + if ($iExtraItemPos < \count($aBodyStructure)) { $aDispList = $aBodyStructure[$iExtraItemPos]; - if (\is_array($aDispList) && 1 < \count($aDispList)) - { - if (!\is_string($aDispList[0])) - { + if (\is_array($aDispList) && 1 < \count($aDispList)) { + if (!\is_string($aDispList[0])) { return null; } $sDisposition = $aDispList[0]; - if (\is_array($aDispList[1])) - { + if (\is_array($aDispList[1])) { $aDispositionParams = self::getKeyValueListFromArrayList($aDispList[1]); $sFileName = self::decodeAttrParameter($aDispositionParams, 'filename', $sCharset); } @@ -536,13 +492,13 @@ class BodyStructure $oStructure = new self; $oStructure->sContentType = \strtolower($sContentTypeMain.'/'.$sContentTypeSub); + $oStructure->aContentTypeParams = $aContentTypeParams; $oStructure->sCharset = $sCharset; - $oStructure->aBodyParams = $aBodyParams; $oStructure->sContentID = $sContentID; $oStructure->sDescription = $sDescription; $oStructure->sMailEncodingName = \strtolower($sMailEncodingName); $oStructure->sDisposition = \strtolower($sDisposition); - $oStructure->sFileName = \MailSo\Base\Utils::Utf8Clear($sFileName ?: $sName); + $oStructure->sFileName = \MailSo\Base\Utils::Utf8Clear($sFileName); $oStructure->iSize = $iSize; $oStructure->sPartID = $sPartID; $oStructure->aSubParts = $aSubParts; @@ -571,12 +527,9 @@ class BodyStructure { $aDict = array(); $iLen = \count($aList); - if (0 === ($iLen % 2)) - { - for ($iIndex = 0; $iIndex < $iLen; $iIndex += 2) - { - if (\is_string($aList[$iIndex]) && \is_string($aList[$iIndex + 1])) - { + if (0 === ($iLen % 2)) { + for ($iIndex = 0; $iIndex < $iLen; $iIndex += 2) { + if (\is_string($aList[$iIndex]) && \is_string($aList[$iIndex + 1])) { $aDict[\strtolower($aList[$iIndex])] = $aList[$iIndex + 1]; } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php index 2f3f5635a..b4ac54835 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Attachment.php @@ -99,6 +99,7 @@ class Attachment implements \JsonSerializable 'Uid' => (string) $this->iUid, 'MimeIndex' => (string) $this->oBodyStructure->PartID(), 'MimeType' => $this->oBodyStructure->ContentType(), + 'MimeTypeParams' => $this->oBodyStructure->ContentTypeParameters(), 'FileName' => \MailSo\Base\Utils::SecureFileName($this->oBodyStructure->FileName(true)), 'EstimatedSize' => $this->oBodyStructure->EstimatedSize(), 'Cid' => $this->oBodyStructure->ContentID(), diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php index 70e7c740b..6cc00245a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php @@ -11,8 +11,8 @@ namespace MailSo\Mail; -use \MailSo\Base\Utils; -use \MailSo\Imap\Enumerations\FetchType; +use MailSo\Base\Utils; +use MailSo\Imap\Enumerations\FetchType; /** * @category MailSo 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 10ef808da..b03a7e10b 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 @@ -151,15 +151,15 @@ class Attachment $oAttachmentPart->Headers->append( new Header(Enumerations\Header::CONTENT_TYPE, - $this->ContentType().';'. - (($oContentTypeParameters) ? ' '.$oContentTypeParameters->ToString() : '') + $this->ContentType(). + ($oContentTypeParameters ? '; '.$oContentTypeParameters : '') ) ); $oAttachmentPart->Headers->append( new Header(Enumerations\Header::CONTENT_DISPOSITION, - ($this->IsInline() ? 'inline' : 'attachment').';'. - (($oContentDispositionParameters) ? ' '.$oContentDispositionParameters->ToString() : '') + ($this->IsInline() ? 'inline' : 'attachment'). + ($oContentDispositionParameters ? '; '.$oContentDispositionParameters : '') ) ); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php index 8a9c84152..ba41125f7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php @@ -200,6 +200,11 @@ class Email implements \JsonSerializable return \trim($sReturn); } + public function __toString() : string + { + return $this->ToString(); + } + #[\ReturnTypeWillChange] public function jsonSerialize() { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php index b909f318f..adda686d7 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/EmailCollection.php @@ -66,6 +66,11 @@ class EmailCollection extends \MailSo\Base\Collection return \implode(', ', $aReturn); } + public function __toString() : string + { + return $this->ToString(); + } + private function parseEmailAddresses(string $sRawEmails) : void { // $sRawEmails = \MailSo\Base\Utils::Trim($sRawEmails); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php index 8f361b586..8681b266a 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php @@ -126,13 +126,6 @@ class Header $this->oParameters->setParameter($sName, $sValue); } - private function wordWrapHelper(string $sValue, string $sGlue = "\r\n ") : string - { - return \trim(\substr(\wordwrap($this->NameWithDelimitrom().$sValue, - 74, $sGlue - ), \strlen($this->NameWithDelimitrom()))); - } - public function __toString() : string { $sResult = $this->sFullValue; @@ -159,11 +152,12 @@ class Header { $oEmailCollection = new EmailCollection($this->sFullValue); if ($oEmailCollection && $oEmailCollection->count()) { - $sResult = $oEmailCollection->ToString(true, false); + $sResult = $oEmailCollection->ToString(true); } } - return $this->NameWithDelimitrom().$this->wordWrapHelper($sResult); + // https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1 + return \wordwrap($this->NameWithDelimitrom() . $sValue, 78, "\r\n "); } public function IsSubject() : bool diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php index 94f4e25a1..ab84d32d8 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Message.php @@ -342,7 +342,7 @@ class Message extends Part } $oPart->Headers->append( new Header(Enumerations\Header::CONTENT_TYPE, - $aAttachments[0]->ContentType().'; '.$oParameters->ToString()) + $aAttachments[0]->ContentType().'; '.$oParameters) ); if ($resource = $aAttachments[0]->Resource()) { @@ -427,11 +427,6 @@ class Message extends Part foreach ($this->aHeadersValue as $sName => $mValue) { if (!($bWithoutBcc && \strtolower(Enumerations\Header::BCC) === \strtolower($sName))) { - if (\is_object($mValue)) { - if ($mValue instanceof EmailCollection || $mValue instanceof Email || $mValue instanceof ParameterCollection) { - $mValue = $mValue->ToString(); - } - } $oRootPart->Headers->SetByName($sName, (string) $mValue); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php index ba4f8dc85..2e20ffe09 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Parameter.php @@ -15,7 +15,7 @@ namespace MailSo\Mime; * @category MailSo * @package Mime */ -class Parameter +class Parameter implements \JsonSerializable { private string $sName; @@ -88,4 +88,18 @@ class Parameter return $this->sName . '="' . $this->sValue . '"'; } + + public function __toString() : string + { + return $this->ToString(); + } + + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array( + 'name' => $this->sName, + 'value' => $this->sValue + ); + } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php index 9028be20e..af34acb40 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/ParameterCollection.php @@ -81,10 +81,29 @@ class ParameterCollection extends \MailSo\Base\Collection $aResult[] = $sLine; } } - return \count($aResult) ? \implode('; ', $aResult) : ''; } + public function __toString() : string + { + return $this->ToString(); + } + +/* + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $aResult = array(); + foreach ($this as $oParam) { + $aResult[$oParam->Name()] = $oParam->Value(); + } + return array( + '@Object' => 'Collection/ParameterCollection', + '@Collection' => $aResult + ); + } +*/ + private function reParseParameters() : void { $aDataToReParse = $this->getArrayCopy(); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php index b15cc24c8..4ecd1c137 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Part.php @@ -51,7 +51,12 @@ class Part { return \trim(\strtolower($this->Headers->ValueByName(Enumerations\Header::CONTENT_TYPE))); } - +/* + public function ContentTypeParameters() : ParameterCollection + { + return $this->Headers->ParametersByName(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE); + } +*/ public function ContentID() : string { return \trim($this->Headers->ValueByName(Enumerations\Header::CONTENT_ID)); From 7858b3e06a41d0dfd13ee965befb0dd2dddfa3f9 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 5 Jan 2023 12:16:52 +0100 Subject: [PATCH 0406/1012] Improved avatars plugin with better cached image handling and custom images --- plugins/avatars/index.php | 128 +++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 35 deletions(-) diff --git a/plugins/avatars/index.php b/plugins/avatars/index.php index c0ad97660..54d026aa5 100644 --- a/plugins/avatars/index.php +++ b/plugins/avatars/index.php @@ -1,4 +1,8 @@ + if ($aResult) { + static::cacheImage($sEmail, $aResult); + } + return $aResult; + } + private static function getUrl(string $sUrl) : ?array { $oHTTP = \SnappyMail\HTTP\Request::factory(/*'socket' or 'curl'*/); @@ -297,17 +351,21 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin $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', $sUrl); - if ($oResponse) { - if (200 === $oResponse->status && \str_starts_with($oResponse->getHeader('content-type'), 'image/')) { - return [ - $oResponse->getHeader('content-type'), - $oResponse->body - ]; + try { + $oResponse = $oHTTP->doRequest('GET', $sUrl); + if ($oResponse) { + if (200 === $oResponse->status && \str_starts_with($oResponse->getHeader('content-type'), 'image/')) { + return [ + $oResponse->getHeader('content-type'), + $oResponse->body + ]; + } + \SnappyMail\Log::notice('Avatar', "error {$oResponse->status} for {$sUrl}"); + } else { + \SnappyMail\Log::warning('Avatar', "failed for {$sUrl}"); } - \SnappyMail\Log::notice('Avatar', "error {$oResponse->status} for {$sUrl}"); - } else { - \SnappyMail\Log::warning('Avatar', "failed for {$sUrl}"); + } catch (\Throwable $e) { + \SnappyMail\Log::notice('Avatar', "error {$e->getMessage()}"); } return null; } From 712cfbb8f4a389621a3461eb5ded3956c2cd747e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 5 Jan 2023 12:18:15 +0100 Subject: [PATCH 0407/1012] Put `:login_name` in change-password PDO default value --- plugins/change-password/drivers/pdo.php | 2 +- plugins/change-password/index.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/change-password/drivers/pdo.php b/plugins/change-password/drivers/pdo.php index 06a601f46..634085188 100644 --- a/plugins/change-password/drivers/pdo.php +++ b/plugins/change-password/drivers/pdo.php @@ -38,7 +38,7 @@ class ChangePasswordDriverPDO \RainLoop\Plugins\Property::NewInstance('pdo_sql')->SetLabel('Statement') ->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT) ->SetDescription('SQL statement (allowed wildcards :email, :oldpass, :newpass, :domain, :username, :login_name).') - ->SetDefaultValue('UPDATE table SET password = :newpass WHERE domain = :domain AND username = :username'), + ->SetDefaultValue('UPDATE table SET password = :newpass WHERE (domain = :domain AND username = :username) OR loginname = :login_name'), \RainLoop\Plugins\Property::NewInstance('pdo_encrypt')->SetLabel('Encryption') ->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECTION) ->SetDefaultValue(array('none', 'bcrypt', 'Argon2i', 'Argon2id', 'SHA256-CRYPT', 'SHA512-CRYPT')) diff --git a/plugins/change-password/index.php b/plugins/change-password/index.php index df6b3807a..76677506f 100644 --- a/plugins/change-password/index.php +++ b/plugins/change-password/index.php @@ -6,8 +6,8 @@ class ChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Change Password', - VERSION = '2.17', - RELEASE = '2022-12-08', + VERSION = '2.18', + RELEASE = '2023-01-05', REQUIRED = '2.23.0', CATEGORY = 'Security', DESCRIPTION = 'Extension to allow users to change their passwords'; From 9a5ad04a6e6a457c4584247e5bf75c3f3cdc3fdc Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 5 Jan 2023 12:29:25 +0100 Subject: [PATCH 0408/1012] Avatars to 48x48 --- .../avatars/images/services/amazon.com.png | Bin 3498 -> 3243 bytes plugins/avatars/images/services/apple.com.png | Bin 1150 -> 1029 bytes plugins/avatars/images/services/asana.com.png | Bin 995 -> 803 bytes plugins/avatars/images/services/dhl.com.png | Bin 0 -> 858 bytes .../images/services/disneyplus.com.png | Bin 0 -> 1498 bytes plugins/avatars/images/services/ea.com.png | Bin 6157 -> 2337 bytes plugins/avatars/images/services/ebay.com.png | Bin 4621 -> 2594 bytes .../avatars/images/services/facebook.com.png | Bin 10088 -> 2361 bytes .../avatars/images/services/github.com.png | Bin 1884 -> 1906 bytes .../avatars/images/services/google.com.png | Bin 2196 -> 1952 bytes .../avatars/images/services/linkedin.com.png | Bin 0 -> 836 bytes .../avatars/images/services/microsoft.com.png | Bin 382 -> 517 bytes .../avatars/images/services/onlive.com.png | Bin 5000 -> 2183 bytes .../avatars/images/services/paypal.com.png | Bin 1070 -> 1453 bytes plugins/avatars/images/services/skype.com.png | Bin 3435 -> 3269 bytes .../images/services/steampowered.com.png | Bin 2697 -> 2544 bytes plugins/avatars/images/services/ted.com.png | Bin 1248 -> 1269 bytes .../avatars/images/services/twitter.com.png | Bin 1104 -> 1364 bytes .../avatars/images/services/youtube.com.png | Bin 2336 -> 3224 bytes 19 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/avatars/images/services/dhl.com.png create mode 100644 plugins/avatars/images/services/disneyplus.com.png create mode 100644 plugins/avatars/images/services/linkedin.com.png diff --git a/plugins/avatars/images/services/amazon.com.png b/plugins/avatars/images/services/amazon.com.png index adfc6f5267b6ac93903774c9d96d6b0a492cb2a5..4717c09c715d212de7cef3c49f267cbe33c24b05 100644 GIT binary patch literal 3243 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 literal 3498 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 diff --git a/plugins/avatars/images/services/apple.com.png b/plugins/avatars/images/services/apple.com.png index 31ca5e8fd519588afe91641b308d4880c32772b6..a787a45802d7b0ef141e3b76512523c5a411be79 100644 GIT binary patch literal 1029 zcmV+g1p51lP)C&0AZ`>?kZcNC5TlDC z_(rZ(gf0TU5TUJzib|p=ShX6h3AGvIDx-#WKrPyQ-QBf zr_eaQ!Y`O>G_^_@9Ph*i+={KZEICp_IYs#cyqu`Pp%B9CqUYD)y!8AXxG)7`!G|*D z5{MTtf*(T&^GyRtx?uJibV~4p-&{a3G~o(SLX&ti3$a>3JAs{<$Xw#%yYX!s0C-)Eo6Nd=BdN$w6`INb@jg(cFAQK0E^0A=z1R{$m}qvO`CdhzY$Zqf9S_xWzF`2V zpd+4ZQ9aDyK@Hp=XVQLRj9m%DR+a0cr5evvI)+Os;%twlCHiUXz>W~Y%(4PVAbRjH z?yn)g{D5~;6Z}r)TB(FHu}MX$zqtm_`Yln0XF~`LU4G|M-(rm2xC-~B>8Xq_@s`Rp z)6caWh%v6kM%<>NfS;psfA$_;#aIZT;gR+~m3=cKy8I)d#9uanAu`rTa5T~K-ZK5U}l#9?*P+dntm+l#8mWv zjX+~en^E9J-vgHl@bqT@zXA0zO}9&0<9qxfc);oe;*SE40NfLE{VcFb(qDND zUYcb0uLVv4myOfp3~;xk?!4wO*&Z;rg7`jQS)SsH0ASV_za0lQ0ShF3pZ5eNI}$tr z?02r-2Yd$XkTf_^6DWCpr{@9lf!SrN_Yac(&d_HgaJ`EY>fNIFCvXJlk@Ux;0x+{> zzj4S<==aZjv?Fl-oTk@RV10A_Zv6Mq9?*(u=KN{eICK)6F5TB|llD#JBq)z}h2KMf6^PLcB4hTS>q{F@e zGzJ7lE z0hsU7=UxH)8W4aIu+}$#1A(0cA9AU6Zvegv48SblS)TxUi88Ap*~4bG!V3UNXMmo- z0Mr1TX136W2Y~02v)59r6l5(oeva0L1?c zJm(`69?P~6)%_LKE&%sSI-3gsSKHT0RNh)dYnSwuuhZao5Do$l7Et_Npv_l8Bo0Cc z@M?j?AB(1?;RylYY$NbN0mPpMR!XWmlkKBedK%Hkj;i9eeiu{hC#w8DPxN186L1sJ zN76|j&T%I`)jz~u%H_?hhNvd5jq&S!hk=iPt-u$OMk@4o6;Unn2H?sHL+=LGOFEKv z?!LblXaF7oZU?R=s?_`u=my@cww&F}rUQ2nmEt!8SGe7$7wD9@~U+fEB|&w!}ehA&T> zTP*-92qq4&0R&+Gx~sxPEvXUY_B}A8r3^DIk>HF@pzY%lkdgErojiZgf9-i7GZGUF zY;z}H0IX8hQqJ{SxWYqLtzPyrkhV(oof2KnSEC@TjS{eDeR$jnz~WyrwgxFMeg@n0zEJuQRXA@hL=(dBZ`JVHBc)Kev}>KJ4sZqsui%i{lb-| z-Rki?P}MDrC|U=uirY7|D_vMs^~W#ke+K>l)##h;b|$Fz000002uVdwM6N<$f>a** AzW@LL delta 755 zcmV;M1&19L)2 zR0s$N+u={(ksb5~h;{^tR zG}VN};z+EyOgl;c|IaBbf}C|%>GYv};5>=!`4*OQ76kNvmmxGj12g~!a64^prK-BB zc4a@-%s&Gx&ka6NfIt;kf$f@r$4t*c*J=RaH*UTlM8R=jSx&d~jgF!y9iuJl zQ6ad;fXO!7?t}BKcza=L_1}}xR~iU08OB;JB|lZUiC>~9PH!QgkU<;fRk~7kPA-o> zi&>mPK+eX0^f>ovkee$Hp9nC8fS!$MaR8T8Ls%T165tI05;zAH0RhT_Tz|-bGYIrN z&zo@aO&tPg{`2Ad1i@Q45K7)2xY`oK^4BrIofZ9o**(Pt%$Byfm-R|Z)dfs#;xAQ4 ziUb(-0N3tfPCwNFI-G`7rCNZ+-cI!10$~WOucuFc^F5$VXG)GLw>McEhY+~9O6%xy zP62+I_|X~rMxga~Gnx?iPD=R;1e9W0xdS%@2Ec5lyxJr?VJ#E|+z{Xg0IYny_9tfn z!@D&9-30*13PhZI8bIO8;O#2{0NGJjoP7EzfY<`v4B%}@h^_+$K(3(6%Qudfa_`U@ z_%(WeVUs@v)ExC?M@;dJ`+s4HN@lK!Ec-0QGzO+ZmR# zm#UfCQ3*T};AS64Yu0M7oMPc3XMDfdzR!X>*(}}-8(oUxfL_F1B(7!!L7?&8lxeIV lKNUa!#}Ca0Xn=nK{062G^tsKJrx^eM002ovPDHLkV1kXpM!f(4 diff --git a/plugins/avatars/images/services/dhl.com.png b/plugins/avatars/images/services/dhl.com.png new file mode 100644 index 0000000000000000000000000000000000000000..9969fed2b01bbbc23c2166659ca669264e97ba5c GIT binary patch literal 858 zcmV-g1Eu_lP)4MbX0kCx`MDv-Cfj^Q#e7-- zmnr`Kxk5T0kjEpgu>_Ul8T(&L2+9rw+P{@pz7--lAPQ4+OrPtLC5qNh6|$9Lw@SnQ zHxhzSSo|g0%%c?4PNht|vOW@GD1Zp8-YwDo(c(ITPewWlxmTQMZz4cl77m<>NtX?+ zYZ2?S8WiZ4p?Nt0;GemO)vFe|MW~5OeZqRqU$a%#u9#v+2~^+97=J1KA2;9>rMlzL z#UAl}L*7xFR~$~}*nN)nuOaPE4Ooyqh|{=r&B6~6$u*6#KnCWBBUv!?{;;fkWuaYs zkKWB`oQ+w!Q)cb6A`S_JZ#$-DdT1-pt^pMY)x&+_mZ9~H#jO^*H%*~_avf6^T6Z<7 z0R&JwPQ&7)f(C{PAe1I%$@@_a&X-m>&{2{lz{K))y)oJk1x z<^M^W}mnL+>)In1~t zk2Tqv-YN$TC%I77d z^>`=Q?qJ^(CNIQPrZWEgVvOXwBC|dU&5yc_AL+AjqssD!7TIvDeUNeJtu71KE3ABM zN0-QHVfy`;bXAis=>a7)fU5jLhAzR{oJAiKIQC@V;Gi&eGNb-#f}LwHx0pQ0fF)fN8@adAQYm*Iq{4kUmQ{%5!BD8q|2K0Q80Lo+#arT z*+oNYTd$WLJ$rLbcEnK|a}1apmSd!d)w4SfgW35I4vnLpE{FdY@&F#d19$)r-~l{< k2k-zMzyo*y5AYoAUj+vvFmvdayZ`_I07*qoM6N<$g7>tCw*UYD literal 0 HcmV?d00001 diff --git a/plugins/avatars/images/services/disneyplus.com.png b/plugins/avatars/images/services/disneyplus.com.png new file mode 100644 index 0000000000000000000000000000000000000000..7f75ec823ffb78288ba627d304acc40829224680 GIT binary patch literal 1498 zcmWlY`9Bj30EgG>U58J4OX=h7FjCG!QaOrY*pgv6DnjJ84o7k`MN(2pOVN^}a;A{9 zM9Lk7h{ed+$XN>czTO|c&+`vF-xxExk%;gHVFH05LZea`i&pu6%Y_!{z*|vBApA9C zcF@AWl0b;ItOKx_1PMvZ%|S{UBq=N`V16Ed{y5Jdq{2pmy3Y`OiW;h zGNPiPyb~H)*s>LISHV1q@86O65ZT#~SHNj+96pM`AT%{0HxJ|E_|yguPgoy8>I0B< zArQdR3nx!OaRLD28h1~9uH$Td!<34mIYDj$=P`xHH_bx}Y(X z%@ixOg>9&_Pq?|GyrH0mvS+}sEJC#%Wq&2dyF@=}+}C)G>E_&V_Qu8sW`*$+0wbI4 zCMl(Nwv=2Q{65?FJ2$Obrpr}U?!#>XrJzK!q&Gkzhl$(WgmV(Uf++flhd&g9v zDRm92!HB1P(dtNYd|Fn%KyGz7NiXSS_Ddgw3cf^?w*;YER(8p<2y@e?gpQ<0$rZkV ze|vcl7VL}Wlm21bv>7@`4KyA5BlANJg_vz~0KDUlHlC>u!skvTK@pxo?C0dGb~k@l#; zZ(eWX=8%U@r47Zgrc2n;k)pcgEO{!M)-ZsB~g^bYBD*woVaX&HTym0F zH%|^}8P)vBQltsQsJ8L0(|U$XVt14AxYO!Ve~azM{qO2ZUmbUic@vx&)pJ>#FWo-b zUN718UbMNL-6eME-&~vOBXfQx%L>_}o^Eq7Vdp7_8CG^#EBepYdj{)!X}m1nUm{>~ zsN!oA%SL$4dwio>dukVg{SK1!iy33KLGG63l~GOcHqBvzOa(tB-&FfndF%P^h0gJ1 YYsK!aArkkvFNRB?8PF-YdJe4r047Un{{R30 literal 0 HcmV?d00001 diff --git a/plugins/avatars/images/services/ea.com.png b/plugins/avatars/images/services/ea.com.png index e88b641c6f749d4683f3eeca84f10492012cbd41..08ed67940bcead8a148d6bdb4ca278d56b269b35 100644 GIT binary patch literal 2337 zcmV++3EuXJP)w0oGXo5x!ss9(Ac})fkjH?pD%OVVZXc9NExy3V*0j4@H=Aynpmx`7 z+O*BOG1;bF?Y3sO*=VvMXsuCecUzOHwN+8a0)n6;%nSnz!_1v;|FBGo512tz_Dg=r z%{{N*Ip24_^PLC7eaO!%SF+1Z=XISO?M8z!ZswqWaNdBgf0nn`o9yfDH4g>?F#sTy zO8XSbm=3u@(GsIlon?%j>~Xu#nat*{sVS+h{r~uc#>#<>wcwnb+{~*j%?mh=EA#dD z&F%GiCr8t$B1YA&j8T3g2F1^(TCIn_Ir;V32i}34{P`SXFgrUtU%lShxyZsZH?r3L!9xCV~nf)9ZBFmN0fG{nUp%$hr|a9qe|&Y4@O)@rXl zXyUg+u8e7$W6OE|ez;4fSS)L0a)s+*lOKXqCUYfQrq*U<&y_^YK5KUNIslK1{17x+ zohKtRvo0#~OUsv6>U6P>ocs_}YPEaOlE+s@M80~}i+PFWNv#uw{16h&=BriJFBFVO zzNolFpOkDlIMK)tAvxLd+5Dnn!?1N^=;NtppJTirdA99p>jF$rX=!OGlgs2)l~pUb zI}(>HEjI~*;F+kSKLkM#yhSCY<{{qv(1%c|7g|03ZOs zqNU4be)Z*-ONc09@|!kog2`lxJm_K!j4^!h!3St+YC>{yGL%Xs&YwRIi9`a0LIF_} z(bv~ES_hcR#=$B zW*jjCypKccccZ4MkCZ}HA122LiQUsZa@$O?A^N; z<>lqz`CI-Y6bj*;cizR8EiXkqp|f-5tw2s*{}&tyoXQs+o&lF4NBw>^8J z_Bz+DU8Bm%=Li5*R8&yc&8~1-b8|CUtyX&GnP#q+> zYdm-`ny{i*7)?!fyB()aor2HjLqS0SR4Nq!AP@-P^y$-gT{$~CI4~C9yLRm&0F;uFGOWJa zB^V6Swr$%*@?;lR$FI{^TzR;_|otA*3)99B-DP(YBU|)j55>{++_? z?77aS^XLETal4=VX$GB6hZ84Gz~OKp7z~bi2i0`(VpNgl-o1OEP{@&%mIfk%R4Tn| z`zEvbtF)Ojop}GFPguOk^a{sugP7p^;h(tofGN@Z>plDadCNmAUA`hitBY;^f9XJs zO5IXeQa0RkPP3-$en7|O;|j)DC}n!;`*)lRO|_={UXVzT7p&KyT1r z&!1oTyC1`c345wCzo_^%tyXjG?s0!nu^}nha%iHOk7erA!^L*Hal|fGR;|o8B_>{- zXynb4CbhlrVohYXHcOT+dtR+pyC({HjYi{MuxRnBsEjQmb5@;3t9|5WrHtb|=@}Ut z$8bY4M`D>atyU_NJ+g}qnOxycO15kOAQ|(t&zwDHWsFMQ_Rw!-G}`v`tl2M&i|@)S zC@eC@Cmdvq1s}pfB^u)s4nI~@QWDYeO(dC~{Mj$QNH8Tno0^{f8<|}044<8ht1yyC zr8j3}WV{-0N_?th;o@WC{?s6^u$U8r;*{3rAGY@Ncpe}04}QF@84Lt+d%fOFUw>cHz`%e$B#IgUjOPW9 zOfKtIC}XawHR`i+x#HyY&d#&e>Cp{Y?A*2-DF)nT!#cA00000NkvXX Hu0mjfY8spM literal 6157 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 diff --git a/plugins/avatars/images/services/ebay.com.png b/plugins/avatars/images/services/ebay.com.png index b38920ca3eaf7547f9388e0d244257b19a67d3a4..943b60e9a12279551951b32a0bc02b71e9d703b6 100644 GIT binary patch literal 2594 zcmV+-3f=XIP)Y-IZ6 z=oI5Md93Oy_H-2%ZIXwKAP^uRPgnM?zB1drqi4rAj(#y4$=ZW!y7xcUW zO18P>!!rZqMT7euIrO=2+;74VQsniukL>7qnCsfeOT>Ox}ORN$9X^f(8!*R-REUYhP1J>K>TI)&u0XPGyv)zr{;alI|pV@tN<+{Fnzv4ItCI5?~ zKQU*LN=Z@(a?X1%C-{GOMZ)xxe&0>1{eiYh1DG^AUW1NHLZfX1<#Tc zM1Az0<97p!b47ATXAeI#@+)(i1U^kbwf3g@rXYur-=Y2hNl@_Z7`la9zspzN#lg?6 z%U8B9uSH<_l#j*G8A2TKKCMrhyNewK zkS!oewu_P=Nh(zurAnhcn`-Od4hr%By|3E&_uV6DczGrpd|>|J zrzQ`jyR&yZ{jJb+0SHPvu2!|eM#DSMJ3*v@F$fI95bb%>+iNy&N?&=ooiCBGD+$00 zhmMVZVfRDBe>_pI)pNP@n=f7ao?S0JD>}#{-{j06d~KFK0whE-kbHMOx_+Sk9b{s@ z5>WMXkBkf-uN{73_COefg6H&-b7T!0k!H(8B!I9$3Y~2L;hk?ZP>wQi_w=s*QpcM8 z%T`YZ>6G;To$G%4_{lwoCYVV9l=r=U{?={#cWn4!ihuzLxMsxJn8>w%BK^^^{@NC~ zMiLEewmmR*x;|xTByqZPJ-joo*p#~VD*KW(NT)P89qs*X<9m1G*cd>N0z>G&ivM1X zb1T+9IC#-CjRm_?=cX|#&+YB8r9$d3iF%E-3fQyFbZ&xtY_3()<*~kV%Z}c+R{V6c zfU#K(Scysj^BF~vKQ(Gvd`HMTpG~v90OuC)C@$qewmMfVO|4sXRyq|Js?e71Ubb`+ z<+K5$$gvRtnH9-oq})6_$b~|4^~yKhd*Z7#Zy`qz1f+%9_ZVqqiD zSU)w(C2jB`MAA@z0b!-k*YmlwF%$(Loe7#P36Z+)wyvv| zzvuRow;BczVoXBq-D^G^0K(xhqNWA?WD>y#=M#q+fk0@Gwt|6^d|)k;)gcwI6y<8Z zkYWk)vf%imC?8jT0CR`&)VH}H;6O4P5d2Aem<$}9F)3_>%r@IFofLsRXW9ql}c zObUW`4vTcL#3!BEJH2V;vM0c4%99>*cVbpR7luC0$-i*))_Aq5O%aexQjwZz7T5Ve z&y?K6L?Fp|S<{lInk~)W@NqvmV`yX;T65OXi7k(hr2{L&JBPM=7uRGEaL!xk=82UB zyX(T@y~2o`V8FsQ41N%+uHb=NGK1P5D@?wK%BK%#N`55kKtVsQxMI z!_hszWS!1=0nRF@qOF1)qOs+J2y@kX!^f4Cj2e+Z!hCvc=<>>%p0{1ld3GM*5@eSl zT>yU+w0vpe_)wP5nU- zsuY+1Y4Ab-6EpLX_d!4}l*<)oD2Ya+Cg(afsJqvY@G$`kshm=|X3RC`Px264BxVBA zB5mEzhAVgd9Jx-mr!_Jslv(Vcu}KqEtD{`@=olXudb)4pQ<-)+|HmWawR*!aoSZBF z_2d)+3&6`PFNB4BCP|BgOiU6X3FD}ViV&M>E6MCYsH5R%jM}q6MWqVb0cPZDpkX$} zLeZ{S9bEI;vB|c*-#;*$2_eDYj^U@TyzKbJ=MFsh*pt6HI1Z8$yjJ%WdzTiNNS5c8 zc!D4~MFgDpAgNfSBgozI88AFw+u)C{$JL{uSJd)P=7wSX4kp*?>RVpWCEFhpuBvkdhO1&Z`l4)Nn9}b zTwO3VwDe``mJTtYA&8=AO%No4I_7jXxuLl}F61Xy^c1gp^~NwX0Mdbd^JN=ezjf{B zcMl(Ua;(va3hkMsAsbaX!Z75w4?v(=??$RQ`lw)!njO5 zUljlM0R5kwp#4t)@jr{|KXmBH7aE`!kA+_Z^uoFR27OmtZ0Fm#od5s;07*qoM6N<$ Ef<=Y^^#A|> 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/facebook.com.png b/plugins/avatars/images/services/facebook.com.png index 2d416f898d2aae613c9fc79f0897ce880393f980..ef15c9325a4ff48450d1162f6321d2a3ea685e80 100644 GIT binary patch literal 2361 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 literal 10088 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 Wc;aeWiumpT0000*V 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 Hu0mjfmLcOz literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV086#aSW-r_4c-**Wm(@;~(R1 zwK-kkJE#!kZ5}v>g+11$IlE1%f?5B;qczJ{JP_UEc$bxZx67+bJ2Ey1En4a9e`kmLDt;|;UeSf!QXVn3aYw-Y!srN5O=&v~!8g}zu{;_|z z7QS?5ue)+a?s;L@0>?Gql{sNkRw$O{xr}6G7JX;y?sk{U#l*!u zZ|>jodA`5j^Zb6_6EVUFyo7*o4}T!1(^>EcQb|LSzZd0!{CB9ca-tk32x6kr7o_-i zNyC4E+|Z8Tcb{km@1;jC9)EEmf~}_b_lE!}*QcLryr=%^v8B`ghI#2{Q|B;e(Trjs zNDq9MP$ha2wlg0t`Wx0l<`ea03I}uu0MMnYn#NlijV2l$9B*%LSUcj8Y=3R;6p0i9 z*z_@}z3SYEyF7OF%$1GyIToljnl{BNC{zF>4gB^1-trI_?*wV4;XTQRzHa#x<|n*oPyB!!i$TX0t;9sX=@?C3(V~35JViI@`q)mTV8w2 z^+uE3qFctmen{CXh4$%?YJb_Jt7sDxW!(~q$!@oZ-$MtIc1FaOYP+loE81oXTk2*N z?A6GWARz$0GK!9cvm#kFA||xOz>HWX6PY%jqWpY=A(tBS(~pnD zG7Dm9>CFS27F5HoZ!*>|`LX~vKO=~|tr5!*W-7c+wPOEzK z!CcaSBhd4qUmAw|vw!6!70{!%R9>7c0{$NUU~aa36fXq?2~ENqEf=;m!`+0JhW_|% zG(ApGxyk@}kAX1ardQ={%pV+I0`B7<-&h>Kmkl(T%%p%oq+HX3wy4T`+E-lO=s-HX zIJhzG$l4eL<>rHL=*!n^8>%Z&B2^4P#IZBuS8M~l+1?SBcYnIZuFWov+kWnO;yP#o zSWDE_leqd7*iY7p5hpMDZ}`*cjltM{1LanO=*C2ks!@>#Qg0)d@2{=R`Teu)=>e?3 zW^(P~&_@%A=(l{ggD@89>y{olvNpfgFg$xL4hjLjR{-`ybdZ$@1K>pCD^v9nvso$) zh31Ecf7ciKQh&Z7$?N}k6RO86k@q#rlXo^vTSlYVTO15T@_8EVxsQ z`@w^j2ZWPpYvO{Z)O%D{KTGU(1e6_e`F5AOiw6LGcY^)pxEKZA+&r@2W}2Efed^sF z6{y=~;D7#Bczf@;vZ+o`@I9rSOa@mlIBs<~aGV9QUccKxG4JZg)_z!dsjY3dP2NiK z!0OlpV8Cr6iQ}(c11VEMC-wt(_d6F0gj9>@?eEn|(j8NA$3QK7(@wvO)u zBpKion5J#Hl7#!!>+mymAP0cTv6v#JcQ6VC`C_s{p$bPma28Nr+6z$B8~OI0 zTulkvN}?0An=MI~ORF_Bv)#Mi`cB;iE8lQ|wLa(Ot*g~avfv!}iU~Wz;iQ&t7WNGV bN*v<9Fz|jUHKK#i00000NkvXXu0mjf7n-xs delta 4260 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 Hu0mjf9~zuN literal 1070 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 DVF6d- literal 3435 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 N002ovPDHLkV1ju=ilqPm diff --git a/plugins/avatars/images/services/steampowered.com.png b/plugins/avatars/images/services/steampowered.com.png index de610701022a85be849724c38117e764de40ca5a..13c4d526f19293deeb0988833b9fa90387b77394 100644 GIT binary patch literal 2544 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 WU&sEBduajy0000mxcMW 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 KMNUMnLSTZ}lRmou literal 2336 zcmV+*3E%dKP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf2(?K>K~!i%?U`F_ zlvNPNeeuCJRJ$IcT-V7a2-IPH~^sK%a>bw{`~op zOeRg%om3PM6veGZjT$X0R;-Zb=4Jyxd)>Nq=}{u>?d=vf0M7>Jd7jOI`KAvSrQOld zksj4GDhddMCB}>yV{t)D;8L_}YHF;X-$jcSnH&aic0A3RHQVNr9FQ+qu)xL@8(&2O zK^a^LMhUEeBH?h@7$t`d8*cKEBS#wJeh?s$1DqJc5|bxSmXeYZS-*b0jp-^C4Fng! zolp>^Py%;@NCr;P^70DHa)6@kMWbegY#>tzJ2>lNf@_+`DU&I!hF_QyLPRu(N+3e zKwO-l05Rd{Uk8M=ic6rt;>C;8(*J?_<>>Q94e%v3e17 zJYs|-`}GsK>MHy7b4EUBkbNhH4U^cUNzyT4f<(ucrq+AzSz9|Yd$#Qt+pu2ZCr@?? zs8b+zYE!4$rpdvB^RnE0bEoVL86xcu+%J)P?=$exDN`i&{BshWHB-Eq)9ttQ_17f+ z<^nr3fHKA`nuqcN!Muq_CrWa_0MYbf%xU^Lm%{lNFjx;ju_vA|3nX89QR0ghN^FT* zedPzz_~klj`tDn4Q%^|Dn=4684CcZC6bJ-UdTZ8Xl#LG@7|4Zyacg9c%>a7%A&EZz zSjt^hX{-UD(CUw*VbeD$K+~p4BKTQBUwOAD_5ps$~P971^?*L7Wl*l7UD1yi{G~2 zq{a$*<)UEMA_Z!EV3F9+l9XBXMVQ>N!}=*|-uK^|jFSrr_Kd*O0tAMOSq zb2*fsGsj#G_lq4#{c*!j!NIP1oy9E3*@$1#r!K7TTOq}) z?_KL})b+zU*SQ>Vl-RW^rJ#=jfy8tI3lsu`ap9kTwi74!vpH!KA||5OUYh~Zx_`g4 zpU_)@5`XwcVfFw#2f@0I!gY0)B=W1ELC|7?uG@XyZm(2Ul`H zAd5YfdB6T*Z98Gz12^r^izZ;>Uwn~C4y}9lnk5*6+a)$^Fu9-pQ6Ovf#xk4K~b;|boR{-JE9T5I{ z30sX)#;xqx&=XxN9}ox&Sv98!Dsv-tWuT~FC;f(Qy;a(EGK#?zDk`l5(o$NQDqcD+ z14Q2ujRUSZ4G6`JOk{OLV}!OJ2sH&FMp|whXnc6=15#W1u*DF15Ic68$U%WXD2N0c zn}#$vjn%Y%AjsG#8%RUnzS5FDIRm0&GC;H-(r%pmxb^d@M8WJ}=O6&reB`irk+2n2 z=AeU1%YF)WEj_8m+ix?aSlAZl8wJupsL;pz^pO*qE&*cy+2xjd&)pJ9XB>bqj~gsR z`u$x`m)kT%x;5aqtWt|+vEPOcl{y|@EzNe@rNY-;dYS3dxXN~o=#nL)C#=*Q$2n523;wBXrUzphlt{l)`4vrsZ zfylR$S81G}2y%c`>FJ?F^__R5`nAnG#$iDoSoCMF0W+R;=x0v(rI(oer}NLVL+tXy z3gQ&^UVM=;3&Pg!eZK6y@PgCj+GaB+x8L+Q*PofEY-6?U=KI%iYdJY>7gxC`FA#2@ zoX`(vpDnx2J@>$whgAKm;Bp-g3@4wjjSjytP-aE_1GR9J~E?>i#a7 z1W~6zAcD63(@&)8<@r*sFEz((z)3m&S}XWhy?YnTKWKbp&u6{t_1Uj>&1& Date: Thu, 5 Jan 2023 16:27:13 +0100 Subject: [PATCH 0409/1012] Improvements for #686 --- dev/Styles/@Boot.css | 10 +++++----- dev/Styles/Main.less | 2 +- dev/Styles/User/Compose.less | 2 +- dev/Styles/User/Layout.less | 8 +++----- dev/Styles/User/MessageList.less | 2 +- integrations/nextcloud/snappymail/css/embed.css | 1 - 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/dev/Styles/@Boot.css b/dev/Styles/@Boot.css index 6febd89e8..059c251fe 100644 --- a/dev/Styles/@Boot.css +++ b/dev/Styles/@Boot.css @@ -53,11 +53,11 @@ html, body { -webkit-touch-callout: none; font-size: var(--main-font-size, 14px); line-height: 1.25; /* 1 */ - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; + position: relative; +} +body#rl-app { + height: 100vh; + width: 100vw; } #rl-loading, #rl-loading-error { diff --git a/dev/Styles/Main.less b/dev/Styles/Main.less index 8cbae31a4..effb4065d 100644 --- a/dev/Styles/Main.less +++ b/dev/Styles/Main.less @@ -76,7 +76,7 @@ dialog:not(.animate) { */ .rl-mobile dialog { margin: 0 auto; - max-height: calc(100vh - 100px); + max-height: calc(100vh - 86px); width: 100%; } diff --git a/dev/Styles/User/Compose.less b/dev/Styles/User/Compose.less index 786b43739..2964b2784 100644 --- a/dev/Styles/User/Compose.less +++ b/dev/Styles/User/Compose.less @@ -9,7 +9,7 @@ #V-PopupsCompose { height: calc(100vh - 52px); max-width: 1000px; - width: 98%; + width: 99%; .modal-body { padding: 0; diff --git a/dev/Styles/User/Layout.less b/dev/Styles/User/Layout.less index 04d98fbee..7f3a7b4ae 100644 --- a/dev/Styles/User/Layout.less +++ b/dev/Styles/User/Layout.less @@ -9,7 +9,7 @@ position: absolute; top: 0; right: 0; - bottom: 0; + bottom: 10px; left: 0; padding: 0; margin: 0; @@ -96,10 +96,8 @@ html:not(.rl-left-panel-disabled) #rl-left > .resizer, } #V-MailMessageList { - position: absolute; - top: 0; - bottom: @rlBottomMargin; - left: 0; + position: relative; + height: 100%; width: 50%; } diff --git a/dev/Styles/User/MessageList.less b/dev/Styles/User/MessageList.less index 56b3a822f..be28b04c6 100644 --- a/dev/Styles/User/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -22,7 +22,7 @@ html.rl-no-preview-pane { } .messageList { - height: calc(100% - 50px); + height: calc(100% - 60px); background-color: #fff; border: 1px solid var(--border-color, #aaa); border-radius: var(--border-radius, 5px); diff --git a/integrations/nextcloud/snappymail/css/embed.css b/integrations/nextcloud/snappymail/css/embed.css index d790145b9..4167e4f41 100644 --- a/integrations/nextcloud/snappymail/css/embed.css +++ b/integrations/nextcloud/snappymail/css/embed.css @@ -5,7 +5,6 @@ This stylesheet is used when SnappyMail runs in embed mode. max-height: 100%; } #content #rl-app { - position: relative; min-width: 100%; min-height: 100%; } From 29854311f90c7b024c3ef54612905087a25f2b28 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 10:16:43 +0100 Subject: [PATCH 0410/1012] Don't `use \` --- snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php | 2 +- .../RainLoop/Providers/AddressBook/Classes/Contact.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php index 3a36c8cd7..368f0f99e 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php @@ -2,7 +2,7 @@ namespace RainLoop\Plugins; -use \RainLoop\Enumerations\PluginPropertyType; +use RainLoop\Enumerations\PluginPropertyType; class Property implements \JsonSerializable { 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 9b8fd15bf..3d88ba81d 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,7 +2,7 @@ namespace RainLoop\Providers\AddressBook\Classes; -use \Sabre\VObject\Component\VCard; +use Sabre\VObject\Component\VCard; class Contact implements \JsonSerializable { From cf1296050750aee4e52743a896174083dae692c3 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 12:28:07 +0100 Subject: [PATCH 0411/1012] Improved cache handling --- dev/Common/Cache.js | 2 +- dev/Common/Folders.js | 93 ++++++++++++------- dev/Remote/AbstractFetch.js | 18 ++-- dev/Remote/User/Fetch.js | 46 --------- dev/Stores/User/Messagelist.js | 3 +- dev/View/Popup/Domain.js | 20 ++-- dev/sieve.js | 24 ++--- .../app/libraries/MailSo/Mail/MailClient.php | 18 ++-- .../MailSo/Mail/MessageListParams.php | 14 +++ .../0.0.0/app/libraries/RainLoop/Actions.php | 16 +++- .../libraries/RainLoop/Actions/Messages.php | 33 ++++--- .../app/libraries/RainLoop/ServiceActions.php | 11 ++- 12 files changed, 153 insertions(+), 145 deletions(-) diff --git a/dev/Common/Cache.js b/dev/Common/Cache.js index 5d69af953..9a56ccbdf 100644 --- a/dev/Common/Cache.js +++ b/dev/Common/Cache.js @@ -62,7 +62,7 @@ export const * @returns {?FolderModel} */ getFolderFromCacheList = folderFullName => - FOLDERS_CACHE[folderFullName] ? FOLDERS_CACHE[folderFullName] : null, + FOLDERS_CACHE[folderFullName] || null, /** * @param {string} folderFullName diff --git a/dev/Common/Folders.js b/dev/Common/Folders.js index 0d3d15720..ba1bf0e13 100644 --- a/dev/Common/Folders.js +++ b/dev/Common/Folders.js @@ -9,11 +9,16 @@ import { SettingsUserStore } from 'Stores/User/Settings'; import { FolderUserStore } from 'Stores/User/Folder'; import { MessagelistUserStore } from 'Stores/User/Messagelist'; import { getNotification } from 'Common/Translator'; -import { Settings, } from 'Common/Globals'; +import { Settings } from 'Common/Globals'; import { serverRequest } from 'Common/Links'; import Remote from 'Remote/User/Fetch'; +import { b64EncodeJSONSafe } from 'Common/Utils'; +import { SettingsGet } from 'Common/Globals'; +import { SUB_QUERY_PREFIX } from 'Common/Links'; +import { AppUserStore } from 'Stores/User/App'; + export const sortFolders = folders => { @@ -28,34 +33,41 @@ sortFolders = folders => { }, /** - * @param {?Function} fCallback - * @param {string} folder - * @param {Array=} list = [] + * @param {object} params */ -fetchFolderInformation = (fCallback, folder, list = []) => { - let fetch = !arrayLength(list); - const uids = [], - folderFromCache = getFolderFromCacheList(folder); +messageList = params => { + const +// folder = getFolderFromCacheList(params.folder.fullName), + folder = getFolderFromCacheList(params.folder), + folderHash = folder?.hash || ''; - if (!fetch) { - list.forEach(messageListItem => { - MessageFlagsCache.getFor(folder, messageListItem.uid) || uids.push(messageListItem.uid); - messageListItem.threads.forEach(uid => { - MessageFlagsCache.getFor(folder, uid) || uids.push(uid); - }); - }); - fetch = uids.length; + params = Object.assign({ + offset: 0, + limit: SettingsUserStore.messagesPerPage(), + search: '', + uidNext: folder?.uidNext || 0, // Used to check for new messages + sort: FolderUserStore.sortMode() + }, params); + if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { + params.useThreads = 1; + } else { + params.threadUid = 0; } - if (fetch) { - Remote.request('FolderInformation', fCallback, { - Folder: folder, - FlagsUids: uids, - UidNext: folderFromCache?.uidNext || 0 // Used to check for new messages - }); - } else if (SettingsUserStore.useThreads()) { - MessagelistUserStore.reloadFlagsAndCachedMessage(); + let sGetAdd = ''; + if (folderHash) { + params.hash = folderHash + '-' + SettingsGet('AccountHash'); + sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + b64EncodeJSONSafe(params); + params = {}; } + + Remote.abort('MessageList'); + Remote.request('MessageList', + null, + params, + 60000, // 60 seconds before aborting + sGetAdd + ); }, /** @@ -127,8 +139,22 @@ refreshFoldersInterval = 300000, */ folderInformation = (folder, list) => { if (folder?.trim()) { - fetchFolderInformation( - (iError, data) => { + let fetch = !arrayLength(list); + const uids = [], + folderFromCache = getFolderFromCacheList(folder); + + if (!fetch) { + list.forEach(messageListItem => { + MessageFlagsCache.getFor(folder, messageListItem.uid) || uids.push(messageListItem.uid); + messageListItem.threads.forEach(uid => { + MessageFlagsCache.getFor(folder, uid) || uids.push(uid); + }); + }); + fetch = uids.length; + } + + if (fetch) { + Remote.request('FolderInformation', (iError, data) => { if (!iError && data.Result) { const result = data.Result, folderFromCache = getFolderFromCacheList(result.Folder); @@ -159,15 +185,20 @@ folderInformation = (folder, list) => { if (folderFromCache.fullName === FolderUserStore.currentFolderFullName()) { MessagelistUserStore.reload(); } else if (getFolderInboxName() === folderFromCache.fullName) { - Remote.messageList(null, {Folder: getFolderInboxName()}, true); +// messageList({folder: getFolderFromCacheList(getFolderInboxName())}); + messageList({folder: getFolderInboxName()}); } } } } - }, - folder, - list - ); + }, { + Folder: folder, + FlagsUids: uids, + UidNext: folderFromCache?.uidNext || 0 // Used to check for new messages + }); + } else if (SettingsUserStore.useThreads()) { + MessagelistUserStore.reloadFlagsAndCachedMessage(); + } } }, diff --git a/dev/Remote/AbstractFetch.js b/dev/Remote/AbstractFetch.js index 8978331d2..546fc26f9 100644 --- a/dev/Remote/AbstractFetch.js +++ b/dev/Remote/AbstractFetch.js @@ -130,11 +130,6 @@ export class AbstractFetchRemote sGetAdd ? null : (params || {}), undefined === iTimeout ? 30000 : pInt(iTimeout), data => { - let cached = false; - if (data?.Time) { - cached = pInt(data.Time) > Date.now() - start; - } - let iError = 0; if (sAction && oRequests[sAction]) { abort(sAction, 0, 1); @@ -157,9 +152,12 @@ export class AbstractFetchRemote fCallback && fCallback( iError, data, - cached, - sAction, - params + /** + * Responses like "304 Not Modified" are returned as "200 OK" + * This is an attempt to detect if the request comes from cache. + * But when client has wrong date/time, it will fail. + */ + data?.epoch && data.epoch < Math.floor(start / 1000) - 60 ); } ) @@ -203,8 +201,8 @@ export class AbstractFetchRemote } /* let isCached = false, type = ''; - if (data?.Time) { - isCached = pInt(data.Time) > microtime() - start; + if (data?.epoch) { + isCached = data.epoch > microtime() - start; } // backward capability switch (true) { diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index aca3ddf26..80346ca78 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -9,57 +9,11 @@ import { SUB_QUERY_PREFIX } from 'Common/Links'; import { AppUserStore } from 'Stores/User/App'; import { SettingsUserStore } from 'Stores/User/Settings'; -import { FolderUserStore } from 'Stores/User/Folder'; import { AbstractFetchRemote } from 'Remote/AbstractFetch'; class RemoteUserFetch extends AbstractFetchRemote { - /** - * @param {Function} fCallback - * @param {object} params - * @param {boolean=} bSilent = false - */ - messageList(fCallback, params, bSilent = false) { - const - sFolderFullName = pString(params.Folder), - folder = getFolderFromCacheList(sFolderFullName), - folderHash = folder?.hash || ''; - - params = Object.assign({ - offset: 0, - limit: SettingsUserStore.messagesPerPage(), - search: '', - uidNext: folder?.uidNext || 0, // Used to check for new messages - sort: FolderUserStore.sortMode(), - Hash: folderHash + SettingsGet('AccountHash') - }, params); - params.Folder = sFolderFullName; - if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { - params.useThreads = 1; - } else { - params.threadUid = 0; - } - - let sGetAdd = ''; - - if (folderHash && (!params.search || !params.search.includes('is:'))) { - sGetAdd = 'MessageList/' + - SUB_QUERY_PREFIX + - '/' + - b64EncodeJSONSafe(params); - params = {}; - } - - bSilent || this.abort('MessageList'); - this.request('MessageList', - fCallback, - params, - 60000, // 60 seconds before aborting - sGetAdd - ); - } - /** * @param {?Function} fCallback * @param {string} sFolderFullName diff --git a/dev/Stores/User/Messagelist.js b/dev/Stores/User/Messagelist.js index 64f0f94a1..ca5862429 100644 --- a/dev/Stores/User/Messagelist.js +++ b/dev/Stores/User/Messagelist.js @@ -293,7 +293,8 @@ MessagelistUserStore.reload = (bDropPagePosition = false, bDropCurrentFolderCach MessagelistUserStore.loading(false); }, { - Folder: FolderUserStore.currentFolderFullName(), +// folder: FolderUserStore.currentFolder() ? self.currentFolder().fullName : ''), + folder: FolderUserStore.currentFolderFullName(), offset: iOffset, limit: SettingsUserStore.messagesPerPage(), search: MessagelistUserStore.listSearch(), diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index a3a0f6287..fcec744dc 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -210,7 +210,15 @@ export class DomainPopupView extends AbstractViewPopup { createOrAddCommand() { this.saving(true); Remote.request('AdminDomainSave', - this.onDomainCreateOrSaveResponse.bind(this), + iError => { + this.saving(false); + if (iError) { + this.savingError(getNotification(iError)); + } else { + DomainAdminStore.fetch(); + this.close(); + } + }, Object.assign(domainToParams(this), { Create: this.edit() ? 0 : 1 }) @@ -263,16 +271,6 @@ export class DomainPopupView extends AbstractViewPopup { }); } - onDomainCreateOrSaveResponse(iError) { - this.saving(false); - if (iError) { - this.savingError(getNotification(iError)); - } else { - DomainAdminStore.fetch(); - this.close(); - } - } - clearTesting() { this.testing(false); this.testingDone(false); diff --git a/dev/sieve.js b/dev/sieve.js index 5c21ab374..33a8dec9e 100644 --- a/dev/sieve.js +++ b/dev/sieve.js @@ -55,13 +55,11 @@ window.Sieve = { deleteScript: script => { serverError(false); Remote.request('FiltersScriptDelete', - (iError, data) => { - if (iError) { - setError(data?.ErrorMessageAdditional || getNotification(iError)); - } else { - scripts.remove(script); - } - }, + (iError, data) => + iError + ? setError(data?.ErrorMessageAdditional || getNotification(iError)) + : scripts.remove(script) + , {name:script.name()} ); }, @@ -69,13 +67,11 @@ window.Sieve = { setActiveScript(name) { serverError(false); Remote.request('FiltersScriptActivate', - (iError, data) => { - if (iError) { - setError(data?.ErrorMessageAdditional || iError) - } else { - scripts.forEach(script => script.active(script.name() === name)); - } - }, + (iError, data) => + iError + ? setError(data?.ErrorMessageAdditional || iError) + : scripts.forEach(script => script.active(script.name() === name)) + , {name:name} ); } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index dcca293d0..f3ada3268 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -582,11 +582,11 @@ class MailClient $sSerializedHash = ''; $sSerializedLog = ''; if ($bUseCacheAfterSearch) { - $sSerializedHash = 'GetUids/'. - ($bUseSort ? 'S' . $sSort : 'N').'/'. - $this->oImapClient->Hash().'/'. - $sFolderName.'/'.$sSearchCriterias; - $sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.''; + $sSerializedHash = 'Get' + . ($bReturnUid ? 'UIDS/' : 'IDS/') + . ($bUseSort ? 'S' . $sSort : 'N') + . "/{$this->oImapClient->Hash()}/{$sFolderName}/{$sSearchCriterias}"; + $sSerializedLog = "\"{$sFolderName}\" / {$sSort} / {$sSearchCriterias}"; $sSerialized = $oCacher->Get($sSerializedHash); if (!empty($sSerialized)) { @@ -596,11 +596,9 @@ class MailClient \is_array($aSerialized['Uids']) ) { if ($this->oLogger) { - $this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); - } - if (\is_array($aSerialized['Uids'])) { - return $aSerialized['Uids']; + $this->oLogger->Write('Get Serialized '.($bReturnUid?'UIDS':'IDS').' from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); } + return $aSerialized['Uids']; } } } @@ -631,7 +629,7 @@ class MailClient ))); if ($this->oLogger) { - $this->oLogger->Write('Save Serialized UIDS to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']'); + $this->oLogger->Write('Save Serialized '.($bReturnUid?'UIDS':'IDS').' to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']'); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php index 5a6ce68a3..d3b622ff4 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php @@ -51,4 +51,18 @@ class MessageListParams // 0 > $oParams->iLimit // 999 < $oParams->iLimit } + + public function hash() : string + { + return \md5(\implode('-', [ + $this->sFolderName, + $this->iOffset, + $this->iLimit, + $this->bHideDeleted ? '1' : '0', + $this->sSearch, + $this->bUseSort ? $this->sSort : '', + $this->bUseThreads ? $this->iThreadUid : '', + $this->iPrevUidNext + ])); + } } 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 5f6a82b5e..f30f26c19 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -408,6 +408,12 @@ class Actions return $this->oMailClient; } + public function ImapClient(): \MailSo\Imap\ImapClient + { +// $this->initMailClientConnection(); + return $this->MailClient()->ImapClient(); + } + // Stores data in AdditionalAccount else MainAccount public function LocalStorageProvider(): Providers\Storage { @@ -1066,9 +1072,9 @@ class Actions return \md5($sKey . $this->oConfig->Get('cache', 'index', '')); } - public function cacheByKey(string $sKey, bool $bForce = false): bool + public function cacheByKey(string $sKey): bool { - if ($sKey && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) { + if ($sKey && $this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)) { \MailSo\Base\Http::ServerUseCache( $this->etag($sKey), 1382478804, @@ -1080,11 +1086,11 @@ class Actions return false; } - public function verifyCacheByKey(string $sKey, bool $bForce = false): void + public function verifyCacheByKey(string $sKey): void { - if ($sKey && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) { + if ($sKey && $this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)) { \MailSo\Base\Http::checkETag($this->etag($sKey)); -// $this->cacheByKey($sKey, $bForce); +// \MailSo\Base\Http::checkLastModified(1382478804); } } 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 3b97f5430..2641479c9 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 @@ -24,12 +24,12 @@ trait Messages $oParams = new \MailSo\Mail\MessageListParams; $sRawKey = $this->GetActionParam('RawKey', ''); - $aValues = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey), true); + $aValues = $sRawKey ? \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey), true) : null; + $sHash = ''; if ($aValues && 6 < \count($aValues)) { - $this->verifyCacheByKey($sRawKey); - -// $oParams->sHash = (string) $aValues['Hash']; - $oParams->sFolderName = (string) $aValues['Folder']; + // GET + $sHash = (string) $aValues['hash']; + $oParams->sFolderName = (string) $aValues['folder']; $oParams->iLimit = $aValues['limit']; $oParams->iOffset = $aValues['offset']; $oParams->sSearch = (string) $aValues['search']; @@ -42,7 +42,8 @@ trait Messages $oParams->iThreadUid = $aValues['threadUid']; } } else { - $oParams->sFolderName = $this->GetActionParam('Folder', ''); + // POST + $oParams->sFolderName = $this->GetActionParam('folder', ''); $oParams->iOffset = $this->GetActionParam('offset', 0); $oParams->iLimit = $this->GetActionParam('limit', 10); $oParams->sSearch = $this->GetActionParam('search', ''); @@ -60,6 +61,16 @@ trait Messages $oAccount = $this->initMailClientConnection(); + if ($sHash) { + $oInfo = $this->ImapClient()->FolderStatusAndSelect($oParams->sFolderName); + $aRequestHash = \explode('-', $sHash); + $sFolderHash = $oInfo->getHash($this->ImapClient()->Hash()); + $sHash = $oParams->hash() . '-' . $sFolderHash; + if ($aRequestHash[0] == $sFolderHash) { + $this->verifyCacheByKey($sHash); + } + } + try { if (!$this->Config()->Get('imap', 'use_thread', true)) { @@ -75,17 +86,15 @@ trait Messages } $oMessageList = $this->MailClient()->MessageList($oParams); + if ($sHash) { + $this->cacheByKey($sHash); + } + return $this->DefaultResponse($oMessageList); } catch (\Throwable $oException) { throw new ClientException(Notifications::CantGetMessageList, $oException); } - - if ($oMessageList) { - $this->cacheByKey($sRawKey); - } - - return $this->DefaultResponse($oMessageList); } public function DoSaveMessage() : array diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 3898c0e69..4b162948c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -160,14 +160,17 @@ class ServiceActions $aResponse['Action'] = $sAction ?: 'Unknown'; - if (\is_array($aResponse)) { - $aResponse['Time'] = (int) ((\microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000); - } - if (!\headers_sent()) { \header('Content-Type: application/json; charset=utf-8'); } + if (\is_array($aResponse)) { + $aResponse['epoch'] = \time(); +// if ($this->Config()->Get('debug', 'enable', false)) { +// $aResponse['rtime'] = \round(\microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], 3); +// } + } + $sResult = Utils::jsonEncode($aResponse); $sObResult = \ob_get_clean(); From be512a0333a9fcae7238767ae3769e5597be995e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 13:11:27 +0100 Subject: [PATCH 0412/1012] Improved solution for #686 --- dev/Styles/Main.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/Styles/Main.less b/dev/Styles/Main.less index effb4065d..2c1322309 100644 --- a/dev/Styles/Main.less +++ b/dev/Styles/Main.less @@ -76,7 +76,7 @@ dialog:not(.animate) { */ .rl-mobile dialog { margin: 0 auto; - max-height: calc(100vh - 86px); +/* max-height: calc(100vh - 86px);*/ width: 100%; } From d3b716cfda6e63fed82d52899fa4a6e8d81fd9e6 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 14:06:33 +0100 Subject: [PATCH 0413/1012] I broke Remote.messageList() in previous commit --- dev/Common/Folders.js | 47 ++-------------------------------------- dev/Remote/User/Fetch.js | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/dev/Common/Folders.js b/dev/Common/Folders.js index ba1bf0e13..4987745e9 100644 --- a/dev/Common/Folders.js +++ b/dev/Common/Folders.js @@ -14,11 +14,6 @@ import { serverRequest } from 'Common/Links'; import Remote from 'Remote/User/Fetch'; -import { b64EncodeJSONSafe } from 'Common/Utils'; -import { SettingsGet } from 'Common/Globals'; -import { SUB_QUERY_PREFIX } from 'Common/Links'; -import { AppUserStore } from 'Stores/User/App'; - export const sortFolders = folders => { @@ -32,44 +27,6 @@ sortFolders = folders => { } }, -/** - * @param {object} params - */ -messageList = params => { - const -// folder = getFolderFromCacheList(params.folder.fullName), - folder = getFolderFromCacheList(params.folder), - folderHash = folder?.hash || ''; - - params = Object.assign({ - offset: 0, - limit: SettingsUserStore.messagesPerPage(), - search: '', - uidNext: folder?.uidNext || 0, // Used to check for new messages - sort: FolderUserStore.sortMode() - }, params); - if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { - params.useThreads = 1; - } else { - params.threadUid = 0; - } - - let sGetAdd = ''; - if (folderHash) { - params.hash = folderHash + '-' + SettingsGet('AccountHash'); - sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + b64EncodeJSONSafe(params); - params = {}; - } - - Remote.abort('MessageList'); - Remote.request('MessageList', - null, - params, - 60000, // 60 seconds before aborting - sGetAdd - ); -}, - /** * @param {Array=} aDisabled * @param {Array=} aHeaderLines @@ -185,8 +142,8 @@ folderInformation = (folder, list) => { if (folderFromCache.fullName === FolderUserStore.currentFolderFullName()) { MessagelistUserStore.reload(); } else if (getFolderInboxName() === folderFromCache.fullName) { -// messageList({folder: getFolderFromCacheList(getFolderInboxName())}); - messageList({folder: getFolderInboxName()}); +// Remote.messageList(null, {folder: getFolderFromCacheList(getFolderInboxName())}, true); + Remote.messageList(null, {folder: getFolderInboxName()}, true); } } } diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index 80346ca78..612d9465b 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -9,11 +9,52 @@ import { SUB_QUERY_PREFIX } from 'Common/Links'; import { AppUserStore } from 'Stores/User/App'; import { SettingsUserStore } from 'Stores/User/Settings'; +import { FolderUserStore } from 'Stores/User/Folder'; import { AbstractFetchRemote } from 'Remote/AbstractFetch'; class RemoteUserFetch extends AbstractFetchRemote { + /** + * @param {Function} fCallback + * @param {object} params + * @param {boolean=} bSilent = false + */ + messageList(fCallback, params, bSilent = false) { + const + // folder = getFolderFromCacheList(params.folder.fullName), + folder = getFolderFromCacheList(params.folder), + folderHash = folder?.hash || ''; + + params = Object.assign({ + offset: 0, + limit: SettingsUserStore.messagesPerPage(), + search: '', + uidNext: folder?.uidNext || 0, // Used to check for new messages + sort: FolderUserStore.sortMode() + }, params); + if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { + params.useThreads = 1; + } else { + params.threadUid = 0; + } + + let sGetAdd = ''; + if (folderHash) { + params.hash = folderHash + '-' + SettingsGet('AccountHash'); + sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + b64EncodeJSONSafe(params); + params = {}; + } + + bSilent || this.abort('MessageList'); + this.request('MessageList', + fCallback, + params, + 60000, // 60 seconds before aborting + sGetAdd + ); + } + /** * @param {?Function} fCallback * @param {string} sFolderFullName From 51493578882ca6c769ca23cd5b00dfa3218a9d4d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 14:25:25 +0100 Subject: [PATCH 0414/1012] Bugfix: Class "RainLoop\Actions\Notifications" not found #839 --- .../v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php index eb5c35a13..4fb554fdb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php @@ -4,6 +4,7 @@ namespace RainLoop\Actions; use RainLoop\Enumerations\PluginPropertyType; use RainLoop\Exceptions\ClientException; +use RainLoop\Notifications; trait AdminExtensions { From 4572d3f43d5a1353f07c08f9c6d37ff828477d30 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 9 Jan 2023 21:21:59 +0100 Subject: [PATCH 0415/1012] Bugfix: undefined $sValue --- snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php index 8681b266a..985e71905 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Header.php @@ -157,7 +157,7 @@ class Header } // https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1 - return \wordwrap($this->NameWithDelimitrom() . $sValue, 78, "\r\n "); + return \wordwrap($this->NameWithDelimitrom() . $sResult, 78, "\r\n "); } public function IsSubject() : bool From 9c9eea0dac132020196270d5792a8e9de36b9b8d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 10 Jan 2023 09:10:20 +0100 Subject: [PATCH 0416/1012] Bugfix: auth_logging failed #489 --- 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 f30f26c19..b081836e9 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -596,7 +596,7 @@ class Actions { $sLine = $this->oConfig->Get('logs', 'auth_logging_format', ''); if (!empty($sLine)) { - $this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount, false, $aAdditionalParams)); + $this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount, false, $aAdditionalParams), \LOG_WARNING); } if (($this->oConfig->Get('logs', 'auth_logging', false) || $this->oConfig->Get('logs', 'auth_syslog', false)) && \openlog('snappymail', 0, \LOG_AUTHPRIV)) { From 07dae9dd30e874f7485a94ddadd589854c28066d Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 11 Jan 2023 08:43:34 +0100 Subject: [PATCH 0417/1012] Resolve ReCaptcha CSP #841 --- plugins/recaptcha/index.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/recaptcha/index.php b/plugins/recaptcha/index.php index bf30f23ea..d4935b8e8 100644 --- a/plugins/recaptcha/index.php +++ b/plugins/recaptcha/index.php @@ -6,8 +6,8 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin NAME = 'reCaptcha', AUTHOR = 'SnappyMail', URL = 'https://snappymail.eu/', - VERSION = '2.13', - RELEASE = '2022-12-08', + VERSION = '2.14', + RELEASE = '2023-01-11', REQUIRED = '2.23', CATEGORY = 'General', LICENSE = 'MIT', @@ -141,10 +141,12 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin public function ContentSecurityPolicy(\SnappyMail\HTTP\CSP $CSP) { - $CSP->script[] = 'https://www.google.com/recaptcha/'; +// $CSP->script[] = 'https://www.google.com/recaptcha/'; $CSP->script[] = 'https://www.gstatic.com/recaptcha/'; - $CSP->frame[] = 'https://www.google.com/recaptcha/'; - $CSP->frame[] = 'https://recaptcha.google.com/recaptcha/'; + $CSP->script[] = 'https://www.recaptcha.net/recaptcha/'; +// $CSP->frame[] = 'https://www.google.com/recaptcha/'; +// $CSP->frame[] = 'https://recaptcha.google.com/recaptcha/'; + $CSP->frame[] = 'https://www.recaptcha.net/recaptcha/'; } } From 2321abcf47c5e75ee024ec831ff70a06166a0037 Mon Sep 17 00:00:00 2001 From: cm-schl <63400209+cm-schl@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:46:05 +0100 Subject: [PATCH 0418/1012] Improve german translation --- .../v/0.0.0/app/localization/de-DE/user.json | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 806286772..aa8e29cc9 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 @@ -33,7 +33,7 @@ "SERVER_MESSAGE": "Server-Nachricht", "YES": "Ja", "NO": "Nein", - "INACTIVE": "Inactive", + "INACTIVE": "Inaktiv", "DEFAULT": "Standard" }, "LOGIN": { @@ -99,7 +99,7 @@ "MENU_SELECT_SEEN": "Gelesen", "MENU_SELECT_FLAGGED": "Markiert", "MENU_SELECT_UNFLAGGED": "Nicht markiert", - "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", + "DOWNLOAD_ALL_ATTACHMENTS": "Alle Anhänge herunterladen", "EMPTY_LIST": "Liste ist leer.", "EMPTY_SEARCH_LIST": "Keine Treffer für Ihre Suche.", "SEARCH_RESULT_FOR": "Suchergebnisse für \"%SEARCH%\"", @@ -152,7 +152,7 @@ "SPAM_SCORE": "Spam-Score", "HAS_VIRUS_WARNING": "WARNUNG: Virus erkannt", "TAGS": "Stichworte", - "NEW_TAG": "New tag" + "NEW_TAG": "Neues Schlagwort" }, "MESSAGE_TAGS": { "$important": "Wichtig", @@ -226,7 +226,7 @@ "NO_ATTACHMENTS_HERE_DESC": "Keine Anhänge vorhanden.", "ATTACHMENTS_ERROR_DESC": "Warnung! Nicht alle Anhänge wurden hochgeladen.", "ATTACHMENTS_UPLOAD_ERROR_DESC": "Es wurden noch nicht alle Anhänge hochgeladen.", - "ALLOW_SPELLCHECK": "Enable browser spelling checker", + "ALLOW_SPELLCHECK": "Rechtschreibprüfung durch den Browser zulassen", "BUTTON_REQUEST_READ_RECEIPT": "Empfangsbestätigung anfordern", "BUTTON_MARK_AS_IMPORTANT": "Als Wichtig markieren", "BUTTON_REQUEST_DSN": "Übermittlungsstatus anfordern" @@ -386,7 +386,7 @@ "SETTINGS_GENERAL": { "LEGEND_GENERAL": "Allgemein", "LANGUAGE": "Sprache", - "HOUR_FORMAT": "Hour format", + "HOUR_FORMAT": "Stundenformat", "IDENTITY": "Identität", "LAYOUT": "Layout", "LAYOUT_NO_SPLIT": "Keine Aufteilung", @@ -414,8 +414,8 @@ "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_SIMPLE_ATTACHMENTS": "Simple attachments list", - "LIST_MESSAGES_GROUPED": "List messages grouped (when sorting by date or from)" + "LIST_SIMPLE_ATTACHMENTS": "Vereinfachte Liste der Anhänge", + "LIST_MESSAGES_GROUPED": "Nachrichten bei Sortierung nach Datum oder Absender gruppieren" }, "SETTINGS_CONTACTS": { "LABEL_CONTACTS_AUTOSAVE": "Empfänger automatisch zu Ihrem Adressbuch hinzuzufügen", @@ -429,7 +429,7 @@ "SETTINGS_THEMES": { "LEGEND_THEMES": "Themen", "LABEL_BACKGROUND": "Hintergrund", - "LABEL_FONTS": "Fonts", + "LABEL_FONTS": "Schriftarten", "BUTTON_UPLOAD_BACKGROUND_IMAGE": "Hintergrundbild hochladen (JPG, PNG)", "ERROR_FILE_IS_TOO_BIG": "Datei ist zu groß", "ERROR_FILE_TYPE_ERROR": "Ungültiger Dateityp (nur JPG und PNG)", @@ -451,7 +451,7 @@ "HELP_SHOW_HIDE_FOLDER": "Ordner ein-\/ausblenden", "HELP_CHECK_FOR_NEW_MESSAGES": "Auf neue Nachrichten prüfen\/nicht prüfen", "HIDE_UNSUBSCRIBED": "Nicht abonnierte Ordner ausblenden", - "UNHIDE_KOLAB_FOLDERS": "Dehide Kolab Ordner", + "UNHIDE_KOLAB_FOLDERS": "Kolab Ordner anzeigen", "TYPE_CALENDAR": "Kalender", "TYPE_CONFIGURATION": "Konfiguration", "TYPE_CONTACTS": "Kontakte", @@ -467,7 +467,7 @@ "BUTTON_ADD_ACCOUNT": "Konto hinzufügen", "BUTTON_ADD_IDENTITY": "Identität hinzufügen", "LOADING_PROCESS": "Aktualisiere Kontenliste", - "SHOW_UNREAD": "Show unread messages count" + "SHOW_UNREAD": "Zeige Zahl der ungelesenen Nachrichten aller Konten" }, "SETTINGS_OPENPGP": { "BUTTON_IMPORT_KEY": "Schlüssel importieren", From 5eab70f93553d40c5ee634932febd724ddec47e4 Mon Sep 17 00:00:00 2001 From: cm-schl <63400209+cm-schl@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:46:17 +0100 Subject: [PATCH 0419/1012] Improve italian translation --- snappymail/v/0.0.0/app/localization/it-IT/user.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 a4d940e5b..a87def6a1 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 @@ -33,7 +33,7 @@ "SERVER_MESSAGE": "Server message", "YES": "Sì", "NO": "No", - "INACTIVE": "Inactive", + "INACTIVE": "Inattivo", "DEFAULT": "predefinito" }, "LOGIN": { @@ -99,7 +99,7 @@ "MENU_SELECT_SEEN": "Letti", "MENU_SELECT_FLAGGED": "Preferiti", "MENU_SELECT_UNFLAGGED": "Non preferiti", - "DOWNLOAD_ALL_ATTACHMENTS": "Download all attachments", + "DOWNLOAD_ALL_ATTACHMENTS": "Scarica tutti allegati", "EMPTY_LIST": "Lista vuota", "EMPTY_SEARCH_LIST": "Nessun messaggio corrisponde alla tua ricerca", "SEARCH_RESULT_FOR": "Risultati di ricerca per \"%SEARCH%\"", @@ -226,7 +226,7 @@ "NO_ATTACHMENTS_HERE_DESC": "Nessun allegato.", "ATTACHMENTS_ERROR_DESC": "Attenzione! Non tutti gli allegati sono stati caricati.", "ATTACHMENTS_UPLOAD_ERROR_DESC": "Non tutti gli allegati sono stati caricati al momento.", - "ALLOW_SPELLCHECK": "Enable browser spelling checker", + "ALLOW_SPELLCHECK": "Abilita il controllo ortografico del browser", "BUTTON_REQUEST_READ_RECEIPT": "Richiedi conferma di lettura", "BUTTON_MARK_AS_IMPORTANT": "Marca come importante", "BUTTON_REQUEST_DSN": "Richiedi conferma del ricevimento" @@ -386,7 +386,7 @@ "SETTINGS_GENERAL": { "LEGEND_GENERAL": "Generali", "LANGUAGE": "Lingua", - "HOUR_FORMAT": "Hour format", + "HOUR_FORMAT": "Formato orario", "IDENTITY": "Identità", "LAYOUT": "Struttura", "LAYOUT_NO_SPLIT": "A pagina intera", @@ -467,7 +467,7 @@ "BUTTON_ADD_ACCOUNT": "Aggiungi account", "BUTTON_ADD_IDENTITY": "Aggiungi identità", "LOADING_PROCESS": "Aggiornamento della lista degli account...", - "SHOW_UNREAD": "Show unread messages count" + "SHOW_UNREAD": "Visualizza il numero di messaggi non letti dei account" }, "SETTINGS_OPENPGP": { "BUTTON_IMPORT_KEY": "Importa chiave", @@ -516,7 +516,7 @@ "LABEL_CLOSE_COMPOSE": "Chiudi finestra di scrittura" }, "NOTIFICATIONS": { - "REQUEST_ERROR": "Request failed", + "REQUEST_ERROR": "Richiesta fallita", "REQUEST_ABORTED": "Request is aborted", "REQUEST_TIMEOUT": "Request timed out", "INVALID_TOKEN": "Token invalido", From 6923ddebe84742b855337279e443dad28f94262c Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 12 Jan 2023 08:58:11 +0100 Subject: [PATCH 0420/1012] Bugfix: MessageCollection->totalEmails was incorrect on search --- snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index f3ada3268..02ff6de71 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -765,8 +765,8 @@ class MailClient } } - if (\count($aUids)) { - $oMessageCollection->totalEmails = \count($aUids); + $oMessageCollection->totalEmails = \count($aUids); + if ($oMessageCollection->totalEmails) { $aUids = \array_slice($aUids, $oParams->iOffset, $oParams->iLimit); $this->MessageListByRequestIndexOrUids($oMessageCollection, new SequenceSet($aUids), $aAllThreads); } From 7b29dca2619f7164fa4e0a95a253822f16a126c1 Mon Sep 17 00:00:00 2001 From: codiflow <43847817+codiflow@users.noreply.github.com> Date: Thu, 12 Jan 2023 13:03:03 +0100 Subject: [PATCH 0421/1012] Adding a button to clear folder search input field --- dev/Styles/User/FolderList.less | 2 +- dev/Styles/User/MessageList.less | 7 +++++++ dev/View/User/MailBox/MessageList.js | 4 ++++ .../v/0.0.0/app/templates/Views/User/MailFolderList.html | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dev/Styles/User/FolderList.less b/dev/Styles/User/FolderList.less index 346cd8039..e229cf0dd 100644 --- a/dev/Styles/User/FolderList.less +++ b/dev/Styles/User/FolderList.less @@ -15,7 +15,7 @@ } input { - width: calc(100% - 8px); + width: calc(100% - 37px); margin-left: 4px; } diff --git a/dev/Styles/User/MessageList.less b/dev/Styles/User/MessageList.less index be28b04c6..df187721c 100644 --- a/dev/Styles/User/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -79,6 +79,13 @@ html.rl-no-preview-pane { padding-right: 8px; } + .btn.buttonClearFolderSearchInput { + font-size: 14px; + padding-left: 8px; + padding-right: 8px; + margin-bottom: 4px; + } + .second-toolbar { display: flex; flex-shrink: 0; diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 104876975..30e5b29eb 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -818,6 +818,10 @@ export class MailMessageList extends AbstractViewRight { showScreenPopup(AdvancedSearchPopupView, [MessagelistUserStore.mainSearch()]); } + clearFolderSearchClick() { + document.getElementById("folderSearchInput").value = ""; + } + groupSearch(group) { group.search && MessagelistUserStore.mainSearch(group.search); } 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 aa4958b4c..f657142e0 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 @@ -20,7 +20,8 @@
      - + +
        From 686fa209452a44e343b7fa072e9428c922a7d24e Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 12 Jan 2023 15:16:32 +0100 Subject: [PATCH 0422/1012] v2.24.5 --- .../files/usr/local/include/application.ini | 2 +- CHANGELOG.md | 45 ++++++++++++++++ README.md | 32 +++++------ build/plugins.php | 2 + cli/release.php | 2 +- integrations/cloudron/DESCRIPTION.md | 2 +- integrations/cloudron/Dockerfile | 2 +- .../nextcloud/snappymail/appinfo/info.xml | 2 +- integrations/virtualmin/snappymail.pl | 2 +- package.json | 2 +- plugins/avatars/avatars.js | 54 ++++++++++--------- .../Views/Admin/AdminSettingsAbout.html | 2 +- 12 files changed, 99 insertions(+), 50 deletions(-) diff --git a/.docker/release/files/usr/local/include/application.ini b/.docker/release/files/usr/local/include/application.ini index bce5d0f3e..2a8958eea 100644 --- a/.docker/release/files/usr/local/include/application.ini +++ b/.docker/release/files/usr/local/include/application.ini @@ -305,5 +305,5 @@ dev_email = "" dev_password = "" [version] -current = "2.24.4" +current = "2.24.5" saved = "Sun, 18 Dec 2022 22:10:48 +0000" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0457f06..2cd8494e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +## 2.24.5 – 2023-01-12 + +## Added +- Support for search criterias ON, SENTON, SENTSINCE and SENTBEFORE +- New 'forward as attachment' glyph +- NC integration app metadata: add links to admin and dev docs by @p-bo + [#820](https://github.com/the-djmaze/snappymail/pull/820) +- Clicking on messagelist grouped date/from searches on that date/from + [#815](https://github.com/the-djmaze/snappymail/pull/815) +- Nextcloud Improved file picker layout + [#825](https://github.com/the-djmaze/snappymail/pull/825) + +## Changed +- Move release.php to cli/release.php +- Moved sort options "FROM" direct below "DATE" for better understanding +- Added DAV path error to log for + [#822](https://github.com/the-djmaze/snappymail/pull/822) +- Resolve layout issues by changing from `fixed` to `relative` + [#686](https://github.com/the-djmaze/snappymail/pull/686) +- Cache handling of messagelist changed due to etag issues +- Improve german and italian translation by @cm-schl + [#846](https://github.com/the-djmaze/snappymail/pull/846) + +## Fixed +- Searching on Nextcloud search #787 + [#787](https://github.com/the-djmaze/snappymail/pull/787) +- Workaround another Nextcloud disallowed .htaccess + [#790](https://github.com/the-djmaze/snappymail/pull/790) +- Compact display of folders in nextcloud by @makoehr + [#824](https://github.com/the-djmaze/snappymail/pull/824) +- Admin -> Contacts PDO test failed when using different AddressBookInterface +- KolabAddressBook errors +- Forgot debug js/css setting change in Nextcloud +- Typed property MailSo\Mail\MessageListParams::$sSearch must not be accessed before initialization +- Unable to change font/text colour when composing message using Safari + [#826](https://github.com/the-djmaze/snappymail/pull/826) +- auth_logging failed + [#489](https://github.com/the-djmaze/snappymail/pull/489) +- Class "RainLoop\Actions\Notifications" not found + [#839](https://github.com/the-djmaze/snappymail/pull/839) + +### Removed +- 'set-version' argument for release.php + + ## 2.24.4 – 2022-12-30 ## Added diff --git a/README.md b/README.md index 09d9b3059..3420609ac 100644 --- a/README.md +++ b/README.md @@ -141,26 +141,26 @@ RainLoop 1.17 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.170.153 | 82.149 | -|app.js |4.207.787 | 412.196 | +|admin.js |2.170.153 | 82.218 | +|app.js |4.207.787 | 412.596 | |boot.js | 868.735 | 2.427 | -|libs.js | 658.812 | 191.117 | -|sieve.js | 0 | 86.018 | +|libs.js | 658.812 | 191.301 | +|sieve.js | 0 | 85.960 | |polyfills.js | 334.608 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |8.240.095 | 774.192 | +|TOTAL |8.240.095 | 774.787 | |js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli | |--------------- |--------: |--------: |------: |------: |--------: |--------: | -|admin.min.js | 256.831 | 40.838 | 73.606 | 13.444 | 60.877 | 12.045 | -|app.min.js | 515.367 | 189.971 |139.456 | 63.463 |110.485 | 54.430 | +|admin.min.js | 256.831 | 40.756 | 73.606 | 13.419 | 60.877 | 12.018 | +|app.min.js | 515.367 | 190.149 |139.456 | 63.499 |110.485 | 54.471 | |boot.min.js | 84.659 | 1.518 | 26.998 | 919 | 23.643 | 747 | -|libs.min.js | 584.772 | 90.689 |180.901 | 33.597 |155.182 | 30.118 | -|sieve.min.js | 0 | 41.909 | 0 | 10.476 | 0 | 9.461 | +|libs.min.js | 584.772 | 90.768 |180.901 | 33.615 |155.182 | 30.170 | +|sieve.min.js | 0 | 41.905 | 0 | 10.477 | 0 | 9.456 | |polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 | -|TOTAL user |1.217.635 | 282.178 |358.761 | 97.979 |299.485 | 85.295 | -|TOTAL user+sieve |1.217.635 | 324.087 |358.761 |108.455 |299.485 | 94.756 | -|TOTAL admin | 959.099 | 133.045 |292.911 | 47.960 |249.877 | 42.910 | +|TOTAL user |1.217.635 | 282.435 |358.761 | 98.033 |299.485 | 85.388 | +|TOTAL user+sieve |1.217.635 | 324.340 |358.761 |108.510 |299.485 | 94.844 | +|TOTAL admin | 959.099 | 133.042 |292.911 | 47.953 |249.877 | 42.935 | For a user its around 70% smaller and faster than traditional RainLoop. @@ -189,12 +189,12 @@ 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 | 82.646 | 46.946 | 17.143 | 14.703 | -|app.min.css | 274.947 | 66.615 | 39.647 | 15.115 | 13.255 | +|app.css | 340.331 | 82.794 | 46.946 | 17.185 | 14.735 | +|app.min.css | 274.947 | 66.702 | 39.647 | 15.146 | 13.290 | |boot.css | | 1.326 | | 664 | 545 | |boot.min.css | | 1.071 | | 590 | 474 | -|admin.css | | 30.277 | | 6.901 | 5.999 | -|admin.min.css | | 24.524 | | 6.238 | 5.503 | +|admin.css | | 30.281 | | 6.906 | 5.999 | +|admin.min.css | | 24.493 | | 6.233 | 5.501 | ### PGP RainLoop uses the old OpenPGP.js v2 diff --git a/build/plugins.php b/build/plugins.php index ab1b5b64d..bf850b802 100755 --- a/build/plugins.php +++ b/build/plugins.php @@ -70,6 +70,7 @@ foreach (glob(ROOT_DIR . '/plugins/*', GLOB_NOSORT | GLOB_ONLYDIR) as $dir) { $tar->compress(Phar::GZ); unlink($tar_destination); rename("{$tar_destination}.gz", $tgz_destination); +/* if (Phar::canWrite()) { $phar_destination = PLUGINS_DEST_DIR . "/{$name}.phar"; @unlink($phar_destination); @@ -79,6 +80,7 @@ foreach (glob(ROOT_DIR . '/plugins/*', GLOB_NOSORT | GLOB_ONLYDIR) as $dir) { unlink($phar_destination); rename("{$phar_destination}.gz", $phar_destination); } +*/ if (isset($options['sign'])) { passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($tgz_destination), $return_var); $manifest_item['pgp_sig'] = trim(preg_replace('/-----(BEGIN|END) PGP SIGNATURE-----/', '', file_get_contents($tgz_destination.'.asc'))); diff --git a/cli/release.php b/cli/release.php index 6ac753d24..a759bf8ae 100755 --- a/cli/release.php +++ b/cli/release.php @@ -3,7 +3,7 @@ define('ROOT_DIR', dirname(__DIR__)); chdir(ROOT_DIR); -$options = getopt('', ['aur','docker','plugins','skip-gulp','debian','nextcloud','owncloud','sign']); +$options = getopt('', ['aur','docker','plugins','skip-gulp','debian','nextcloud','owncloud','cpanel','sign']); if (isset($options['plugins'])) { require(ROOT_DIR . '/build/plugins.php'); diff --git a/integrations/cloudron/DESCRIPTION.md b/integrations/cloudron/DESCRIPTION.md index 396d1936b..fceaddb2c 100644 --- a/integrations/cloudron/DESCRIPTION.md +++ b/integrations/cloudron/DESCRIPTION.md @@ -1,4 +1,4 @@ -This app packages SnappyMail 2.24.4. +This app packages SnappyMail 2.24.5. SnappyMail is a simple, modern, lightweight & fast web-based email client. diff --git a/integrations/cloudron/Dockerfile b/integrations/cloudron/Dockerfile index 960643234..6195ceb9e 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.24.4 +VERSION=2.24.5 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 48def463a..e6f5c6fd6 100644 --- a/integrations/nextcloud/snappymail/appinfo/info.xml +++ b/integrations/nextcloud/snappymail/appinfo/info.xml @@ -3,7 +3,7 @@ snappymail SnappyMail SnappyMail Webmail - 2.24.4 + 2.24.5 agpl SnappyMail, RainLoop Team, Nextgen-Networks, Tab Fitts, Nathan Kinkade, Pierre-Alain Bandinelli { - setIdenticon(msg.from[0], fn); + msg.from?.[0] && setIdenticon(msg.from[0], fn); if (rl.pluginSettingsGet('avatars', 'delay')) { queue.push([msg, fn]); runQueue(); @@ -75,31 +75,33 @@ runQueue = (() => { let item = queue.shift(); while (item) { - let url = getAvatar(item[0]), - uid = getAvatarUid(item[0]); - if (url) { - item[1](url); - item = queue.shift(); - } else if (!avatars.has(uid)) { - 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(uid, url); - item[1](url); - } else { - avatars.set(uid, ''); - } - runQueue(); - }, 'Avatar', { - bimi: 'pass' == from.dkimStatus ? 1 : 0, - email: from.email - }); - break; - } else { - runQueue(); - break; + if (item[0].from) { + let url = getAvatar(item[0]), + uid = getAvatarUid(item[0]); + if (url) { + item[1](url); + item = queue.shift(); + continue; + } else if (!avatars.has(uid)) { + 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(uid, url); + item[1](url); + } else { + avatars.set(uid, ''); + } + runQueue(); + }, 'Avatar', { + bimi: 'pass' == from.dkimStatus ? 1 : 0, + email: from.email + }); + break; + } } + runQueue(); + break; } }).debounce(1000); @@ -204,7 +206,7 @@ view.message.subscribe(msg => { view.viewUserPicVisible(false); if (msg) { - let url = getAvatar(msg), + let url = msg.from?.[0] ? getAvatar(msg) : 0, fn = url => { view.viewUserPic(url); view.viewUserPicVisible(true); 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 8264d22c8..656120ba7 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 @@ -4,7 +4,7 @@
        - 2022 © + 2023 ©
        https://snappymail.eu/
        From bd35a73f5f31d7f998810db67b95403a7ea41ed7 Mon Sep 17 00:00:00 2001 From: codiflow <43847817+codiflow@users.noreply.github.com> Date: Thu, 12 Jan 2023 15:52:54 +0100 Subject: [PATCH 0423/1012] Added search input wrapper div --- .../v/0.0.0/app/templates/Views/User/MailFolderList.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 f657142e0..5dc009132 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 @@ -20,8 +20,10 @@
        - - +
        + + +
          From 177dfcb7e5fa7f191ebe9dee0631525fa3c8b26c Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 12 Jan 2023 16:54:40 +0100 Subject: [PATCH 0424/1012] Improved and solve #847 --- dev/Styles/User/FolderList.less | 2 +- dev/Styles/User/Layout.less | 17 ++++++++++++++ dev/Styles/User/MessageList.less | 22 ------------------- dev/View/User/MailBox/FolderList.js | 4 ++++ dev/View/User/MailBox/MessageList.js | 4 ---- .../templates/Views/User/MailFolderList.html | 6 ++--- 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/dev/Styles/User/FolderList.less b/dev/Styles/User/FolderList.less index e229cf0dd..346cd8039 100644 --- a/dev/Styles/User/FolderList.less +++ b/dev/Styles/User/FolderList.less @@ -15,7 +15,7 @@ } input { - width: calc(100% - 37px); + width: calc(100% - 8px); margin-left: 4px; } diff --git a/dev/Styles/User/Layout.less b/dev/Styles/User/Layout.less index 7f3a7b4ae..0a39a5c17 100644 --- a/dev/Styles/User/Layout.less +++ b/dev/Styles/User/Layout.less @@ -177,6 +177,23 @@ html.rl-side-preview-pane { border-radius: var(--input-border-radius, 3px) !important; } +.search-input-wrp { + position: relative; +} +.search-input-wrp a { + position: absolute; + right: 0; + top: 5px; + margin: 0 7px; + z-index: 100; + opacity: .4; + cursor: pointer; + + &:hover { + opacity: .6; + } +} + /* desktop */ @media screen and (min-width: 1000px) { } diff --git a/dev/Styles/User/MessageList.less b/dev/Styles/User/MessageList.less index df187721c..6259d9785 100644 --- a/dev/Styles/User/MessageList.less +++ b/dev/Styles/User/MessageList.less @@ -79,13 +79,6 @@ html.rl-no-preview-pane { padding-right: 8px; } - .btn.buttonClearFolderSearchInput { - font-size: 14px; - padding-left: 8px; - padding-right: 8px; - margin-bottom: 4px; - } - .second-toolbar { display: flex; flex-shrink: 0; @@ -107,8 +100,6 @@ html.rl-no-preview-pane { .search-input-wrp { flex-grow: 1; - position: relative; - text-align: right; } .inputSearch { @@ -116,19 +107,6 @@ html.rl-no-preview-pane { width: 100%; } - .closeSearch { - position: absolute; - right: 0; - top: 5px; - margin: 0 7px; - z-index: 100; - vertical-align: middle; - opacity: .4; - &:hover { - opacity: .6; - } - } - .b-content { height: 100%; padding: 0; diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index 67d4a12e0..3acab9bd1 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -206,6 +206,10 @@ export class MailFolderList extends AbstractViewLeft { showMessageComposer(); } + clearFolderSearch() { + foldersFilter(''); + } + createFolder() { showScreenPopup(FolderCreatePopupView); } diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 30e5b29eb..104876975 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -818,10 +818,6 @@ export class MailMessageList extends AbstractViewRight { showScreenPopup(AdvancedSearchPopupView, [MessagelistUserStore.mainSearch()]); } - clearFolderSearchClick() { - document.getElementById("folderSearchInput").value = ""; - } - groupSearch(group) { group.search && MessagelistUserStore.mainSearch(group.search); } 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 5dc009132..2c8d3f298 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 @@ -20,9 +20,9 @@
          -
          - - +
          + +
            From e1f8fa509089a5271d7b89505f8f0a8f05492028 Mon Sep 17 00:00:00 2001 From: dominiquefournier Date: Sat, 14 Jan 2023 19:14:04 +0100 Subject: [PATCH 0425/1012] Update fr-FR translations --- .../v/0.0.0/app/localization/fr-FR/user.json | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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 20e4620cd..d2c551f19 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 @@ -152,7 +152,7 @@ "SPAM_SCORE": "Score de spam", "HAS_VIRUS_WARNING": "ATTENTION : virus détecté", "TAGS": "Étiquettes", - "NEW_TAG": "New tag" + "NEW_TAG": "Nouvelle étiquette" }, "MESSAGE_TAGS": { "$important": "Important", @@ -172,16 +172,16 @@ }, "CONTACTS": { "TAB_CONTACT": "Contact", - "TAB_LOCATIONS": "Locations", - "TAB_BUSINESS": "Business", - "LABEL_ORGANIZATION": "Organization", + "TAB_LOCATIONS": "Localisations", + "TAB_BUSINESS": "Métier", + "LABEL_ORGANIZATION": "Entreprise", "LABEL_DEPARTMENT": "Department", - "LABEL_TITLE": "Title", - "TAB_CRYPTO": "Crypto", - "ASK": "Ask", - "NEVER": "Never", - "ALWAYS": "Always", - "ALWAYS_IF_POSSIBLE": "Always if possible", + "LABEL_TITLE": "Titre", + "TAB_CRYPTO": "Chiffre", + "ASK": "Demander", + "NEVER": "Jamais", + "ALWAYS": "Toujours", + "ALWAYS_IF_POSSIBLE": "Toujours si possible", "BUTTON_ADD_CONTACT": "Ajouter un contact", "BUTTON_CREATE_CONTACT": "Créer", "BUTTON_UPDATE_CONTACT": "Modifier", @@ -358,14 +358,14 @@ "TEXT_SWITCHER_SOURCE": "HTML <-> SOURCE" }, "SETTINGS_LABELS": { - "PERSONAL": "Personal", - "GENERAL": "General", + "PERSONAL": "Personnel", + "GENERAL": "Général", "CONTACTS": "Contacts", - "FOLDERS": "Folders", - "ACCOUNTS": "Accounts", - "FILTERS": "Filters", - "SECURITY": "Security", - "THEMES": "Themes" + "FOLDERS": "Dossiers", + "ACCOUNTS": "Comptes", + "FILTERS": "Filtres", + "SECURITY": "Securité", + "THEMES": "Thèmes" }, "SETTINGS_FILTERS": { "LEGEND_FILTERS": "Filtres", @@ -386,7 +386,7 @@ "SETTINGS_GENERAL": { "LEGEND_GENERAL": "Général", "LANGUAGE": "Langue", - "HOUR_FORMAT": "Hour format", + "HOUR_FORMAT": "Format horaire", "IDENTITY": "Identité", "LAYOUT": "Disposition", "LAYOUT_NO_SPLIT": "Pas de séparation", @@ -402,12 +402,12 @@ "USE_THREADS": "Regrouper en conversations", "REPLY_SAME_FOLDER": "Placer les réponses dans le dossier du message", "SHOW_IMAGES": "Toujours afficher les images dans le mail", - "PREFER_HTML": "Prefer HTML to plain text", + "PREFER_HTML": "Préférer le HTML au texte pur", "PREFER_HTML_INFO": "Messages sometimes come in both formats. This option controls whether you want the HTML part or the plain text part to be displayed.", "REMOVE_COLORS": "Supprimer les couleurs d'arrière-plan et de texte du message", "MESSAGE_PER_PAGE": "Messages par page", "MARK_MESSAGE_READ_AFTER": "Marquer le message comme lu après", - "MSG_DEFAULT_ACTION": "Default action", + "MSG_DEFAULT_ACTION": "Action par défaut", "SECONDS": "secondes", "NOTIFICATIONS": "Notifications", "SOUND_NOTIFICATION": "Son de notification", @@ -467,7 +467,7 @@ "BUTTON_ADD_ACCOUNT": "Ajouter un compte", "BUTTON_ADD_IDENTITY": "Ajouter une identité", "LOADING_PROCESS": "Mise à jour de la liste des comptes", - "SHOW_UNREAD": "Show unread messages count" + "SHOW_UNREAD": "Affiche le compteur de messages non lus" }, "SETTINGS_OPENPGP": { "BUTTON_IMPORT_KEY": "Importer la clé", From ad8eccf83ba1e27a78fd7af5427c3f4cf6bd1feb Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 16 Jan 2023 14:10:55 +0100 Subject: [PATCH 0426/1012] Resolve #858 --- dev/Styles/User/Layout.less | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dev/Styles/User/Layout.less b/dev/Styles/User/Layout.less index 0a39a5c17..f38ff4719 100644 --- a/dev/Styles/User/Layout.less +++ b/dev/Styles/User/Layout.less @@ -6,18 +6,15 @@ } #rl-left, #rl-right { - position: absolute; + position: fixed; top: 0; right: 0; bottom: 10px; left: 0; - padding: 0; - margin: 0; - border: 0; - z-index: 0; } #rl-left { + z-index: 0; width: @rlLeftWidth; } @@ -179,6 +176,7 @@ html.rl-side-preview-pane { .search-input-wrp { position: relative; + text-align: right; } .search-input-wrp a { position: absolute; @@ -270,7 +268,6 @@ html.rl-left-panel-disabled { html.rl-no-preview-pane { #V-MailMessageList { - right: @rlBottomMargin !important; width: inherit; } @@ -329,6 +326,6 @@ html.rl-mobile { } &.rl-left-panel-disabled #rl-right { - left: 5px; + left: 0; } } From 61899ce09293a1bfe02f211672c9aa6f89f40fed Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 17 Jan 2023 10:28:19 +0100 Subject: [PATCH 0427/1012] Prepare for custom SMTP credentials for #859 #458 #431 #233 --- .../0.0.0/app/libraries/RainLoop/Actions.php | 4 +- .../libraries/RainLoop/Actions/Accounts.php | 2 - .../libraries/RainLoop/Actions/Messages.php | 2 +- .../libraries/RainLoop/Actions/UserAuth.php | 2 +- .../app/libraries/RainLoop/Model/Account.php | 74 ++++++++++--------- .../RainLoop/Model/AdditionalAccount.php | 26 +++++-- .../Providers/Filters/SieveStorage.php | 2 +- 7 files changed, 63 insertions(+), 49 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 b081836e9..a56d7c443 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -682,8 +682,6 @@ class Actions $aResult = \array_merge($aResult, [ 'Auth' => true, 'Email' => \MailSo\Base\Utils::IdnToUtf8($oAccount->Email()), - 'IncLogin' => $oAccount->IncLogin(), - 'OutLogin' => $oAccount->OutLogin(), 'AccountHash' => $oAccount->Hash(), 'AccountSignMe' => isset($_COOKIE[self::AUTH_SIGN_ME_TOKEN_KEY]), 'MainEmail' => \MailSo\Base\Utils::IdnToUtf8($this->getMainAccountFromToken()->Email()), @@ -1103,7 +1101,7 @@ class Actions if (!$this->MailClient()->IsLoggined()) { try { - $oAccount->ImapConnectAndLoginHelper($this->oPlugins, $this->MailClient()->ImapClient(), $this->oConfig); + $oAccount->ImapConnectAndLogin($this->oPlugins, $this->MailClient()->ImapClient(), $this->oConfig); } catch (\MailSo\Net\Exceptions\ConnectionException $oException) { throw new Exceptions\ClientException(Notifications::ConnectionError, $oException); } catch (\Throwable $oException) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index bad35f18b..91dc8ae02 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -202,8 +202,6 @@ trait Accounts if ($this->switchAccount(\trim($this->GetActionParam('Email', '')))) { $oAccount = $this->getAccountFromToken(); $aResult['Email'] = $oAccount->Email(); - $aResult['IncLogin'] = $oAccount->IncLogin(); - $aResult['OutLogin'] = $oAccount->OutLogin(); $aResult['AccountHash'] = $oAccount->Hash(); $aResult['MainEmail'] = ($oAccount instanceof AdditionalAccount) ? $oAccount->ParentEmail() : ''; 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 2641479c9..e5f1b7f8b 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 @@ -780,7 +780,7 @@ trait Messages $oSmtpClient->SetLogger($this->Logger()); $bUsePhpMail = false; - $oAccount->SmtpConnectAndLoginHelper($this->Plugins(), $oSmtpClient, $this->Config(), $bUsePhpMail); + $oAccount->SmtpConnectAndLogin($this->Plugins(), $oSmtpClient, $this->Config(), $bUsePhpMail); if ($bUsePhpMail) { if (\MailSo\Base\Utils::FunctionCallable('mail')) { 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 71a9a8525..fb5b13efd 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 @@ -450,7 +450,7 @@ trait UserAuth if (!$oImapClient) { $oImapClient = $this->MailClient()->ImapClient(); } - $oAccount->ImapConnectAndLoginHelper($this->Plugins(), $oImapClient, $this->Config()); + $oAccount->ImapConnectAndLogin($this->Plugins(), $oImapClient, $this->Config()); } catch (ClientException $oException) { throw $oException; } catch (\MailSo\Net\Exceptions\ConnectionException $oException) { 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 cfa7f3551..17cf82109 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 @@ -16,6 +16,10 @@ abstract class Account implements \JsonSerializable private string $sPassword = ''; + private string $sSmtpLogin = ''; + + private string $sSmtpPassword = ''; + private string $sProxyAuthUser = ''; private string $sProxyAuthPassword = ''; @@ -32,16 +36,6 @@ abstract class Account implements \JsonSerializable return $this->sName; } - public function ProxyAuthUser() : string - { - return $this->sProxyAuthUser; - } - - public function ProxyAuthPassword() : string - { - return $this->sProxyAuthPassword; - } - public function IncLogin() : string { return $this->oDomain->IncShortLogin() @@ -56,23 +50,8 @@ abstract class Account implements \JsonSerializable public function OutLogin() : string { - return $this->oDomain->OutShortLogin() - ? \MailSo\Base\Utils::GetAccountNameFromEmail($this->sLogin) - : $this->sLogin; - } - - // Deprecated - public function Login() : string - { - \trigger_error('Use \RainLoop\Model\Account->IncLogin()', \E_USER_DEPRECATED); - return $this->IncLogin(); - } - - // Deprecated - public function Password() : string - { - \trigger_error('Use \RainLoop\Model\Account->IncPassword()', \E_USER_DEPRECATED); - return $this->IncPassword(); + $sSmtpLogin = $this->sSmtpLogin ?: $this->sLogin; + return $this->oDomain->OutShortLogin() ? \MailSo\Base\Utils::GetAccountNameFromEmail($sSmtpLogin) : $sSmtpLogin; } public function Domain() : Domain @@ -95,6 +74,11 @@ abstract class Account implements \JsonSerializable $this->sPassword = $sPassword; } + public function SetSmtpPassword(string $sPassword) : void + { + $this->sSmtpLogin = $sPassword; + } + public function SetProxyAuthUser(string $sProxyAuthUser) : void { $this->sProxyAuthUser = $sProxyAuthUser; @@ -116,6 +100,12 @@ abstract class Account implements \JsonSerializable // '', // 4 sClientCert 'name' => $this->sName ]; + if ($this->sSmtpLogin && $this->sSmtpPassword) { + $result['smtp'] = [ + 'user' => $this->sSmtpLogin, + 'pass' => $this->sSmtpPassword + ]; + } if ($this->sProxyAuthUser && $this->sProxyAuthPassword) { $result['proxy'] = [ 'user' => $this->sProxyAuthUser, // 5 @@ -200,19 +190,25 @@ abstract class Account implements \JsonSerializable if (isset($aAccountHash['name'])) { $oAccount->sName = $aAccountHash['name']; } + $oActions->Logger()->AddSecret($oAccount->sPassword); + // init smtp user/password + if (isset($aAccountHash['smtp'])) { + $oAccount->sSmtpLogin = $aAccountHash['smtp']['user']; + $oAccount->sSmtpPassword = $aAccountHash['smtp']['pass']; + $oActions->Logger()->AddSecret($oAccount->sSmtpPassword); + } // init proxy user/password if (isset($aAccountHash['proxy'])) { $oAccount->sProxyAuthUser = $aAccountHash['proxy']['user']; $oAccount->sProxyAuthPassword = $aAccountHash['proxy']['pass']; + $oActions->Logger()->AddSecret($oAccount->sProxyAuthPassword); } - $oActions->Logger()->AddSecret($oAccount->IncPassword()); - $oActions->Logger()->AddSecret($oAccount->ProxyAuthPassword()); } } return $oAccount; } - public function ImapConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool + public function ImapConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Imap\ImapClient $oImapClient, \RainLoop\Config\Application $oConfig) : bool { $oSettings = $this->Domain()->ImapSettings(); $oSettings->timeout = \max($oSettings->timeout, (int) $oConfig->Get('imap', 'timeout', $oSettings->timeout)); @@ -231,10 +227,11 @@ abstract class Account implements \JsonSerializable $oImapClient->Connect($oSettings); $oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $oSettings)); + $oSettings->Password = $this->IncPassword(); 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 + public function SmtpConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool { $oSettings = $this->Domain()->SmtpSettings(); $oSettings->Login = $this->OutLogin(); @@ -249,11 +246,16 @@ abstract class Account implements \JsonSerializable $oSmtpClient->Connect($oSettings, $oSettings->Ehlo); } $oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $oSettings)); - +/* + if ($this->oDomain->OutAskCredentials() && !($this->sSmtpPassword && $this->sSmtpLogin)) { + throw new RequireCredentialsException + } +*/ + $oSettings->Password = $this->sSmtpPassword ?: $this->sPassword; return $this->netClientLogin($oSmtpClient, $oPlugins, $oSettings); } - public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\SieveClient $oSieveClient, \RainLoop\Config\Application $oConfig) + public function SieveConnectAndLogin(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\SieveClient $oSieveClient, \RainLoop\Config\Application $oConfig) { $oSettings = $this->Domain()->SieveSettings(); $oSettings->Login = $this->IncLogin(); @@ -262,6 +264,7 @@ abstract class Account implements \JsonSerializable $oSieveClient->Connect($oSettings); $oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $oSettings)); + $oSettings->Password = $this->IncPassword(); return $this->netClientLogin($oSieveClient, $oPlugins, $oSettings); } @@ -276,9 +279,8 @@ abstract class Account implements \JsonSerializable [cipher_version] => TLSv1.3 ) */ - $oSettings->Password = $this->IncPassword(); - $oSettings->ProxyAuthUser = $this->ProxyAuthUser(); - $oSettings->ProxyAuthPassword = $this->ProxyAuthPassword(); + $oSettings->ProxyAuthUser = $this->sProxyAuthUser; + $oSettings->ProxyAuthPassword = $this->sProxyAuthPassword; $client_name = \strtolower($oClient->getLogName()); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php index d076b016d..f2a61d93c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/AdditionalAccount.php @@ -32,7 +32,10 @@ class AdditionalAccount extends Account $sHash = $oMainAccount->CryptKey(); $aData = $this->jsonSerialize(); $aData['pass'] = \SnappyMail\Crypt::EncryptUrlSafe($aData['pass'], $sHash); // sPassword - if (isset($aAccountHash['proxy'])) { + if (!empty($aData['smtp']['pass'])) { + $aData['smtp']['pass'] = \SnappyMail\Crypt::EncryptUrlSafe($aData['smtp']['pass'], $sHash); + } + if (!empty($aData['proxy']['pass'])) { $aData['proxy']['pass'] = \SnappyMail\Crypt::EncryptUrlSafe($aData['proxy']['pass'], $sHash); // sProxyAuthPassword } $aData['hmac'] = \hash_hmac('sha1', $aData['pass'], $sHash); @@ -49,10 +52,23 @@ class AdditionalAccount extends Account $sHash = $oActions->getMainAccountFromToken()->CryptKey(); // hmac only set when asTokenArray() was used $sPasswordHMAC = $aAccountHash['hmac'] ?? null; - if ($sPasswordHMAC && $sPasswordHMAC === \hash_hmac('sha1', $aAccountHash['pass'], $sHash)) { - $aAccountHash['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['pass'], $sHash); - if (isset($aAccountHash['proxy'])) { - $aAccountHash['proxy']['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['proxy']['pass'], $sHash); + if ($sPasswordHMAC) { + if ($sPasswordHMAC === \hash_hmac('sha1', $aAccountHash['pass'], $sHash)) { + $aAccountHash['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['pass'], $sHash); + if (!empty($aData['smtp']['pass'])) { + $aAccountHash['smtp']['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['smtp']['pass'], $sHash); + } + if (!empty($aData['proxy']['pass'])) { + $aAccountHash['proxy']['pass'] = \SnappyMail\Crypt::DecryptUrlSafe($aAccountHash['proxy']['pass'], $sHash); + } + } else { + $aAccountHash['pass'] = ''; + if (!empty($aData['smtp']['pass'])) { + $aAccountHash['smtp']['pass'] = ''; + } + if (!empty($aData['proxy']['pass'])) { + $aAccountHash['proxy']['pass'] = ''; + } } } return parent::NewInstanceFromTokenArray($oActions, $aAccountHash, $bThrowExceptionOnFalse); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php index eb859de54..de141825c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php @@ -33,7 +33,7 @@ class SieveStorage implements FiltersInterface { $oSieveClient = new \MailSo\Sieve\SieveClient(); $oSieveClient->SetLogger($this->oLogger); - return $oAccount->SieveConnectAndLoginHelper($this->oPlugins, $oSieveClient, $this->oConfig) + return $oAccount->SieveConnectAndLogin($this->oPlugins, $oSieveClient, $this->oConfig) ? $oSieveClient : null; } From 29beb9b75d1c2107df1d62fbf65dd4862232348f Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 17 Jan 2023 11:02:45 +0100 Subject: [PATCH 0428/1012] Improved visibility Admin Domain Test errors --- dev/View/Popup/Domain.js | 36 ++++--------------- .../templates/Views/Admin/PopupsDomain.html | 9 +++-- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index fcec744dc..7babe05b8 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -89,9 +89,6 @@ export class DomainPopupView extends AbstractViewPopup { testingImapError: false, testingSieveError: false, testingSmtpError: false, - testingImapErrorDesc: '', - testingSieveErrorDesc: '', - testingSmtpErrorDesc: '', imapHostFocus: false, sieveHostFocus: false, @@ -142,10 +139,6 @@ export class DomainPopupView extends AbstractViewPopup { }); addSubscribablesTo(this, { - testingImapError: value => value || this.testingImapErrorDesc(''), - testingSieveError: value => value || this.testingSieveErrorDesc(''), - testingSmtpError: value => value || this.testingSmtpErrorDesc(''), - // smart form improvements imapHostFocus: value => value && this.name() && !this.imapHost() && this.imapHost(this.name().replace(/[.]?[*][.]?/g, '')), @@ -226,7 +219,7 @@ export class DomainPopupView extends AbstractViewPopup { } testConnectionCommand() { - this.clearTesting(false); + this.clearTesting(); // https://github.com/the-djmaze/snappymail/issues/477 AskPopupView.credentials('IMAP', 'GLOBAL/TEST').then(credentials => { if (credentials) { @@ -240,29 +233,14 @@ export class DomainPopupView extends AbstractViewPopup { (iError, oData) => { this.testing(false); if (iError) { - this.testingImapError(true); - this.testingSieveError(true); - this.testingSmtpError(true); + this.testingImapError(getNotification(iError)); + this.testingSieveError(getNotification(iError)); + this.testingSmtpError(getNotification(iError)); } else { this.testingDone(true); - this.testingImapError(true !== oData.Result.Imap); - this.testingSieveError(true !== oData.Result.Sieve); - this.testingSmtpError(true !== oData.Result.Smtp); - - if (this.testingImapError() && oData.Result.Imap) { - this.testingImapErrorDesc(''); - this.testingImapErrorDesc(oData.Result.Imap); - } - - if (this.testingSieveError() && oData.Result.Sieve) { - this.testingSieveErrorDesc(''); - this.testingSieveErrorDesc(oData.Result.Sieve); - } - - if (this.testingSmtpError() && oData.Result.Smtp) { - this.testingSmtpErrorDesc(''); - this.testingSmtpErrorDesc(oData.Result.Smtp); - } + this.testingImapError(true !== oData.Result.Imap ? oData.Result.Imap : false); + this.testingSieveError(true !== oData.Result.Sieve ? oData.Result.Sieve : false); + this.testingSmtpError(true !== oData.Result.Smtp ? oData.Result.Smtp : false); } }, params diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html index 086401273..b732551a1 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html @@ -20,9 +20,10 @@
            +
            @@ -135,9 +136,10 @@
            diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html index dc0fa8b31..2493b15e7 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionMoveToFolder.html @@ -8,14 +8,14 @@ name: 'Checkbox', params: { label: 'POPUPS_FILTER/MARK_AS_READ_LABEL', - value: actionMarkAsRead + value: markAsRead } }">
            diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNone.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNone.html index a9992de2e..ad5a4ca84 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNone.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionNone.html @@ -3,7 +3,7 @@ name: 'Checkbox', params: { label: 'POPUPS_FILTER/STOP_LABEL', - value: actionNoStop + value: stop } }">
            diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionReject.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionReject.html index f3783c4bb..d96367be2 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionReject.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionReject.html @@ -6,7 +6,7 @@ name: 'Checkbox', params: { label: 'POPUPS_FILTER/STOP_LABEL', - value: actionNoStop + value: stop } }">
            diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionVacation.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionVacation.html index 1a2275c23..1c84affd4 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionVacation.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsFiltersActionVacation.html @@ -28,7 +28,7 @@ name: 'Checkbox', params: { label: 'POPUPS_FILTER/STOP_LABEL', - value: actionNoStop + value: stop } }"> From f23b7205c6100a4dd044b71c22074c2d37683a57 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 25 Jan 2023 09:28:48 +0100 Subject: [PATCH 0465/1012] Squire Colour to Color --- vendors/squire/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vendors/squire/README.md b/vendors/squire/README.md index 2ce7b803d..437cff617 100644 --- a/vendors/squire/README.md +++ b/vendors/squire/README.md @@ -205,7 +205,7 @@ Returns the path through the DOM tree from the `` element to the current c ### getFontInfo -Returns an object containing the active font family, size, colour and background colour for the the current cursor position, if any are set. The property names are respectively `family`, `size`, `color` and `backgroundColor`. It looks at style attributes to detect this, so will not detect `` tags or non-inline styles. If a selection across multiple elements has been made, it will return an empty object. +Returns an object containing the active font family, size, color and background color for the the current cursor position, if any are set. The property names are respectively `family`, `size`, `color` and `backgroundColor`. It looks at style attributes to detect this, so will not detect `` tags or non-inline styles. If a selection across multiple elements has been made, it will return an empty object. ### createRange @@ -252,7 +252,7 @@ Returns self (the Squire instance). ### saveUndoState Saves an undo checkpoint with the current editor state. Methods that modify the -state (e.g. bold/setHighlightColour/modifyBlocks) will automatically save undo +state (e.g. bold/setHighlightColor/modifyBlocks) will automatically save undo checkpoints; you only need this method if you want to modify the DOM outside of one of these methods, and you want to save an undo checkpoint first. @@ -354,23 +354,23 @@ This method takes one argument: Returns self (the Squire instance). -### setTextColour +### setTextColor -Sets the colour of the selected text. +Sets the color of the selected text. This method takes one argument: -* **colour**: The colour to set. Any [CSS colour value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) is accepted, e.g. '#f00', or 'hsl(0,0,0)'. +* **color**: The color to set. Any [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) is accepted, e.g. '#f00', or 'hsl(0,0,0)'. Returns self (the Squire instance). -### setHighlightColour +### setHighlightColor -Sets the colour of the background of the selected text. +Sets the color of the background of the selected text. This method takes one argument: -* **colour**: The colour to set. Any [CSS colour value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) is accepted, e.g. '#f00', or 'hsl(0,0,0)'. +* **color**: The color to set. Any [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) is accepted, e.g. '#f00', or 'hsl(0,0,0)'. Returns self (the Squire instance). From 528b6cd096f523a41dc71362fb17aec33e085d58 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 25 Jan 2023 09:41:15 +0100 Subject: [PATCH 0466/1012] cleanup some json code --- dev/Knoin/AbstractModel.js | 16 ++++++++-------- dev/Model/Attachment.js | 1 + dev/Model/Message.js | 6 ++++++ .../app/libraries/MailSo/Imap/BodyStructure.php | 2 +- .../0.0.0/app/libraries/MailSo/Mail/Message.php | 6 +++--- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index 66f94e6a2..092466e00 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -80,11 +80,9 @@ export class AbstractModel { } revivePropertiesFromJson(json) { - let model = this.constructor; - if (!model.validJson(json)) { - return false; - } - forEachObjectEntry(json, (key, value) => { + const model = this.constructor, + valid = model.validJson(json); + valid && forEachObjectEntry(json, (key, value) => { if ('@' !== key[0]) try { // key = key[0].toLowerCase() + key.slice(1); switch (typeof this[key]) @@ -103,9 +101,11 @@ export class AbstractModel { this[key] = typeCast(this[key], value); break; case 'undefined': + console.log(`Undefined ${model.name}.${key} set`); this[key] = value; - // fall through - default: + break; +// default: +// console.log((typeof this[key])+` ${model.name}.${key} not revived`); // console.log((typeof this[key])+' '+(model.name)+'.'+key+' not revived'); } } catch (e) { @@ -113,7 +113,7 @@ export class AbstractModel { console.error(e); } }); - return true; + return valid; } } diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index 4ebe3d29f..011dcd271 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -18,6 +18,7 @@ export class AttachmentModel extends AbstractModel { this.checked = ko.observable(true); this.mimeType = ''; +// this.mimeTypeParams = ''; this.fileName = ''; this.fileNameExt = ''; this.fileType = FileType.Unknown; diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 9d4793136..036d7b0b4 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -74,6 +74,7 @@ export class MessageModel extends AbstractModel { this.to = new EmailCollectionModel; this.cc = new EmailCollectionModel; this.bcc = new EmailCollectionModel; + this.sender = new EmailCollectionModel; this.replyTo = new EmailCollectionModel; this.deliveredTo = new EmailCollectionModel; this.body = null; @@ -120,6 +121,11 @@ export class MessageModel extends AbstractModel { readReceipt: '', + autocrypt: '', + // rfc8621 + id: '', +// threadId: '', + hasUnseenSubMessage: false, hasFlaggedSubMessage: false }); diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php index 2fe70bf5a..246231322 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Imap/BodyStructure.php @@ -528,7 +528,7 @@ class BodyStructure implements \JsonSerializable return array( 'mimeIndex' => $this->sPartID, 'mimeType' => $this->sContentType, - 'mimeTypeParams' => $this->aContentTypeParams, +// 'mimeTypeParams' => $this->aContentTypeParams, 'fileName' => \MailSo\Base\Utils::SecureFileName($this->FileName(true)), 'estimatedSize' => $this->EstimatedSize(), 'cId' => $this->sContentID, diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php index 55ac6e176..e6003b9a0 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/Message.php @@ -380,11 +380,11 @@ class Message implements \JsonSerializable . \trim($oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['bodyPartId'].']')) ); if ($sPgpText) { - $oMessage->pgpSigned['Body'] = $sPgpText; + $oMessage->pgpSigned['body'] = $sPgpText; } $sPgpSignatureText = $oFetchResponse->GetFetchValue(FetchType::BODY.'['.$oMessage->pgpSigned['sigPartId'].']'); if ($sPgpSignatureText && 0 < \strpos($sPgpSignatureText, 'BEGIN PGP SIGNATURE')) { - $oMessage->pgpSigned['Signature'] = $oPart->SubParts()[0]->PartID(); + $oMessage->pgpSigned['signature'] = $oPart->SubParts()[0]->PartID(); } */ break; @@ -527,7 +527,7 @@ class Message implements \JsonSerializable // https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.1 'id' => $this->sEmailId, // 'blobId' => $this->sEmailIdBlob, - 'threadId' => $this->sThreadId, +// 'threadId' => $this->sThreadId, // 'mailboxIds' => ['mailboxid'=>true], // 'keywords' => $keywords, 'size' => $this->iSize From ba543c95d7b8f0856ddc27b08dd8dee73adad2fa Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 25 Jan 2023 09:52:30 +0100 Subject: [PATCH 0467/1012] Improved #871 --- dev/Styles/User/Layout.less | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/Styles/User/Layout.less b/dev/Styles/User/Layout.less index bceeadda2..6f2028931 100644 --- a/dev/Styles/User/Layout.less +++ b/dev/Styles/User/Layout.less @@ -174,10 +174,6 @@ html.rl-side-preview-pane { border-radius: var(--input-border-radius, 3px) !important; } -.b-content .search-input-wrp { - margin-right: 8px; -} - .search-input-wrp { position: relative; text-align: right; @@ -195,6 +191,9 @@ html.rl-side-preview-pane { opacity: .6; } } +#rl-left .search-input-wrp { + margin-right: 8px; +} /* desktop */ @media screen and (min-width: 1000px) { From b9ef8ae2c932a3b7f0dcedb63f806dc3c585cb43 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 25 Jan 2023 10:24:18 +0100 Subject: [PATCH 0468/1012] Resolve #866 --- .../libraries/RainLoop/Common/PdoAbstract.php | 188 ++++++------------ .../RainLoop/Providers/AddressBook/Legacy.php | 8 +- .../app/libraries/snappymail/http/stream.php | 1 + 3 files changed, 62 insertions(+), 135 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php index c29fb84f5..aef3bb100 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php @@ -63,80 +63,51 @@ abstract class PdoAbstract */ protected function getPDO() : \PDO { - if ($this->oPDO) - { + if ($this->oPDO) { return $this->oPDO; } - if (!\class_exists('PDO')) - { + if (!\class_exists('PDO')) { throw new \Exception('Class PDO does not exist'); } $sType = $sDsn = $sDbLogin = $sDbPassword = ''; list($sType, $sDsn, $sDbLogin, $sDbPassword) = $this->getPdoAccessData(); - if (!\in_array($sType, static::getAvailableDrivers())) - { + if (!\in_array($sType, static::getAvailableDrivers())) { throw new \Exception('Unknown PDO SQL connection type'); } - if (empty($sDsn)) - { + if (empty($sDsn)) { throw new \Exception('Empty PDO DSN configuration'); } $this->sDbType = $sType; - $oPdo = false; - try - { -// $bCaseFunc = false; - $oPdo = new \PDO($sDsn, $sDbLogin, $sDbPassword); - if ($oPdo) - { - $sPdoType = $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME); - $oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $oPdo = new \PDO($sDsn, $sDbLogin, $sDbPassword); + $sPdoType = $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + $oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - if ('mysql' === $sType && 'mysql' === $sPdoType) - { - $oPdo->exec('SET NAMES utf8mb4 COLLATE utf8mb4_general_ci'); - } -// else if ('sqlite' === $sType && 'sqlite' === $sPdoType && $this->bSqliteCollate) -// { -// if (\method_exists($oPdo, 'sqliteCreateCollation')) -// { -// $oPdo->sqliteCreateCollation('SQLITE_NOCASE_UTF8', array($this, 'sqliteNoCaseCollationHelper')); -// $bCaseFunc = true; -// } -// } -// -// $this->oLogger->Write('PDO:'.$sPdoType.($bCaseFunc ? '/SQLITE_NOCASE_UTF8' : '')); - } - } - catch (\Throwable $oException) - { - throw $oException; - } - - if ($oPdo) - { - $this->oPDO = $oPdo; - } - else - { - throw new \Exception('PDO = false'); +// $bCaseFunc = false; + if ('mysql' === $sType && 'mysql' === $sPdoType) { + $oPdo->exec('SET NAMES utf8mb4 COLLATE utf8mb4_general_ci'); } +// else if ('sqlite' === $sType && 'sqlite' === $sPdoType && $this->bSqliteCollate) { +// if (\method_exists($oPdo, 'sqliteCreateCollation')) { +// $oPdo->sqliteCreateCollation('SQLITE_NOCASE_UTF8', array($this, 'sqliteNoCaseCollationHelper')); +// $bCaseFunc = true; +// } +// } +// $this->oLogger->Write('PDO:'.$sPdoType.($bCaseFunc ? '/SQLITE_NOCASE_UTF8' : '')); + $this->oPDO = $oPdo; return $oPdo; } protected function lastInsertId(?string $sTabelName = null, ?string $sColumnName = null) : string { $mName = null; - if ('pgsql' === $this->sDbType && - null !== $sTabelName && $sColumnName !== null) - { + if ('pgsql' === $this->sDbType && null !== $sTabelName && $sColumnName !== null) { $mName = \strtolower($sTabelName.'_'.$sColumnName.'_seq'); } @@ -160,8 +131,7 @@ abstract class PdoAbstract protected function prepareAndExecute(string $sSql, array $aParams = array(), bool $bMultiplyParams = false, bool $bLogParams = false) : ?\PDOStatement { - if ($this->bExplain && !$bMultiplyParams) - { + if ($this->bExplain && !$bMultiplyParams) { $this->prepareAndExplain($sSql, $aParams); } @@ -169,27 +139,19 @@ abstract class PdoAbstract $this->writeLog($sSql); $oStmt = $this->getPDO()->prepare($sSql); - if ($oStmt) - { + if ($oStmt) { $aLogs = array(); $aRootParams = $bMultiplyParams ? $aParams : array($aParams); - foreach ($aRootParams as $aSubParams) - { - foreach ($aSubParams as $sName => $aValue) - { - if ($bLogParams) - { + foreach ($aRootParams as $aSubParams) { + foreach ($aSubParams as $sName => $aValue) { + if ($bLogParams) { $aLogs[$sName] = $aValue[0]; } - $oStmt->bindValue($sName, $aValue[0], $aValue[1]); } - $mResult = $oStmt->execute() && !$bMultiplyParams ? $oStmt : null; } - - if ($bLogParams && $aLogs) - { + if ($bLogParams && $aLogs) { $this->writeLog('Params: '.\json_encode($aLogs, JSON_UNESCAPED_UNICODE)); } } @@ -200,15 +162,12 @@ abstract class PdoAbstract protected function prepareAndExplain(string $sSql, array $aParams = array()) { $mResult = null; - if (0 === strpos($sSql, 'SELECT ')) - { + if (0 === \strpos($sSql, 'SELECT ')) { $sSql = 'EXPLAIN '.$sSql; $this->writeLog($sSql); $oStmt = $this->getPDO()->prepare($sSql); - if ($oStmt) - { - foreach ($aParams as $sName => $aValue) - { + if ($oStmt) { + foreach ($aParams as $sName => $aValue) { $oStmt->bindValue($sName, $aValue[0], $aValue[1]); } @@ -216,8 +175,7 @@ abstract class PdoAbstract } } - if ($mResult) - { + if ($mResult) { $aFetch = $mResult->fetchAll(\PDO::FETCH_ASSOC); $this->oLogger->WriteDump($aFetch); @@ -231,18 +189,12 @@ abstract class PdoAbstract */ protected function writeLog($mData) { - if ($this->oLogger) - { - if ($mData instanceof \Throwable) - { + if ($this->oLogger) { + if ($mData instanceof \Throwable) { $this->oLogger->WriteException($mData, \LOG_ERR, 'SQL'); - } - else if (\is_scalar($mData)) - { + } else if (\is_scalar($mData)) { $this->oLogger->Write((string) $mData, \LOG_INFO, 'SQL'); - } - else - { + } else { $this->oLogger->WriteDump($mData, \LOG_INFO, 'SQL'); } } @@ -257,18 +209,15 @@ abstract class PdoAbstract protected function getVersion(string $sName) : ?int { $oPdo = $this->getPDO(); - if ($oPdo) - { + if ($oPdo) { $sQuery = 'SELECT MAX(value_int) FROM rainloop_system WHERE sys_name = ?'; $this->writeLog($sQuery); $oStmt = $oPdo->prepare($sQuery); - if ($oStmt->execute(array($sName.'_version'))) - { + if ($oStmt->execute(array($sName.'_version'))) { $mRow = $oStmt->fetch(\PDO::FETCH_NUM); - if ($mRow && isset($mRow[0])) - { + if ($mRow && isset($mRow[0])) { return (int) $mRow[0]; } @@ -283,21 +232,18 @@ abstract class PdoAbstract { $bResult = false; $oPdo = $this->getPDO(); - if ($oPdo) - { + if ($oPdo) { $sQuery = 'DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?;'; $this->writeLog($sQuery); $oStmt = $oPdo->prepare($sQuery); $bResult = !!$oStmt->execute(array($sName.'_version', $iVersion)); - if ($bResult) - { + if ($bResult) { $sQuery = 'INSERT INTO rainloop_system (sys_name, value_int) VALUES (?, ?);'; $this->writeLog($sQuery); $oStmt = $oPdo->prepare($sQuery); - if ($oStmt) - { + if ($oStmt) { $bResult = !!$oStmt->execute(array($sName.'_version', $iVersion)); } } @@ -314,8 +260,7 @@ abstract class PdoAbstract $bResult = true; $oPdo = $this->getPDO(); - if ($oPdo) - { + if ($oPdo) { $aQ = array(); switch ($this->sDbType) { @@ -361,24 +306,17 @@ rl_email text NOT NULL DEFAULT \'\' break; } - if (\count($aQ)) - { + if (\count($aQ)) { try { - foreach ($aQ as $sQuery) - { - if ($bResult) - { - $this->writeLog($sQuery); - $bResult = false !== $oPdo->exec($sQuery); - if (!$bResult) - { - $this->writeLog('Result=false'); - } - else - { - $this->writeLog('Result=true'); - } + foreach ($aQ as $sQuery) { + $this->writeLog($sQuery); + $bResult = false !== $oPdo->exec($sQuery); + if (!$bResult) { + $this->writeLog('Result=false'); + break; + } else { + $this->writeLog('Result=true'); } } } @@ -402,12 +340,10 @@ rl_email text NOT NULL DEFAULT \'\' } catch (\PDOException $oException) { - $this->writeLog($oException); - +// $this->writeLog($oException); try { $this->initSystemTables(); - $iFromVersion = $this->getVersion($sName); } catch (\PDOException $oSubException) @@ -418,33 +354,26 @@ rl_email text NOT NULL DEFAULT \'\' } $bResult = false; - if (\is_int($iFromVersion) && 0 <= $iFromVersion) - { + if (\is_int($iFromVersion) && 0 <= $iFromVersion) { $oPdo = false; - foreach ($aData as $iVersion => $aQuery) - { - if (0 === \count($aQuery)) - { + foreach ($aData as $iVersion => $aQuery) { + if (0 === \count($aQuery)) { continue; } - if (!$oPdo) - { + if (!$oPdo) { $oPdo = $this->getPDO(); $bResult = true; } - if ($iFromVersion < $iVersion && $oPdo) - { + if ($iFromVersion < $iVersion && $oPdo) { try { - foreach ($aQuery as $sQuery) - { + foreach ($aQuery as $sQuery) { $this->writeLog($sQuery); $bExec = $oPdo->exec($sQuery); - if (false === $bExec) - { + if (false === $bExec) { $this->writeLog('Result: false'); $bResult = false; @@ -458,8 +387,7 @@ rl_email text NOT NULL DEFAULT \'\' throw $oException; } - if (!$bResult) - { + if (!$bResult) { break; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Legacy.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Legacy.php index 117570c01..6a125ebfd 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Legacy.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Legacy.php @@ -20,11 +20,9 @@ class Legacy if (\strlen($sValue)) { $oTypes = $oProp['TYPE']; $aTypes = $oTypes ? $oTypes->getParts() : array(); - $pref = 100; - if (0 < $oProp['PREF']) { - $pref = (int) $oProp['PREF']; - } - $aTmp[\substr(1000+$pref,-3) . $sValue] = new Property($iType, $sValue, \implode(',', $aTypes)); + $pref = empty($oProp['PREF']) ? 100 : \min(100, \max(1, $oProp['PREF']->getValue())); + $pref = \str_pad($pref, 3, '0', \STR_PAD_LEFT); + $aTmp[$pref . $sValue] = new Property($iType, $sValue, \implode(',', $aTypes)); } } \ksort($aTmp); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/http/stream.php b/snappymail/v/0.0.0/app/libraries/snappymail/http/stream.php index b01b9beba..565ecf554 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/http/stream.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/http/stream.php @@ -19,6 +19,7 @@ abstract class Stream \ob_implicit_flush(); \ini_set('implicit_flush',1); \ini_set('output_buffering', 0); + \ini_set('display_errors', 0); if ($i = \ob_get_level()) { # Clear buffers: while ($i-- && \ob_end_clean()); From 974350ebee0c914b2f77242d25a9bde6dbdea6d8 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 26 Jan 2023 10:41:55 +0100 Subject: [PATCH 0469/1012] Found more JSON properties to change into JavaScript camelCase --- dev/App/User.js | 2 +- dev/Common/Folders.js | 22 +-- dev/Common/UtilsUser.js | 23 +++ dev/Model/FolderCollection.js | 4 +- dev/Model/Message.js | 6 +- dev/Settings/Admin/About.js | 8 +- dev/Settings/Admin/Domains.js | 10 +- dev/Settings/Admin/Packages.js | 14 +- dev/Settings/Admin/Security.js | 8 +- dev/Settings/User/Accounts.js | 4 +- dev/Settings/User/Folders.js | 4 +- dev/Stores/Admin/Domain.js | 2 +- dev/Stores/User/GnuPG.js | 16 +- dev/Stores/User/Messagelist.js | 8 +- dev/Stores/User/OpenPGP.js | 7 +- dev/View/Popup/Account.js | 2 +- dev/View/Popup/Compose.js | 2 +- dev/View/Popup/Contacts.js | 6 +- dev/View/Popup/Domain.js | 161 ++++++++---------- dev/View/Popup/DomainAlias.js | 10 +- dev/View/Popup/Plugin.js | 18 +- dev/View/User/MailBox/MessageList.js | 42 +---- dev/View/User/MailBox/MessageView.js | 22 +-- plugins/README.md | 9 +- plugins/nextcloud/js/message.js | 4 +- plugins/nextcloud/js/messagelist.js | 5 +- .../0.0.0/app/libraries/RainLoop/Actions.php | 7 +- .../libraries/RainLoop/Actions/Accounts.php | 12 +- .../RainLoop/Actions/AdminDomains.php | 14 +- .../RainLoop/Actions/AdminExtensions.php | 30 ++-- .../RainLoop/Actions/Attachments.php | 4 +- .../libraries/RainLoop/Actions/Contacts.php | 7 +- .../libraries/RainLoop/Actions/Folders.php | 16 +- .../libraries/RainLoop/Actions/Messages.php | 158 ++++++++--------- .../app/libraries/RainLoop/Actions/Pgp.php | 18 +- .../libraries/RainLoop/Actions/Response.php | 2 +- .../app/libraries/RainLoop/Actions/Themes.php | 5 +- .../app/libraries/RainLoop/ActionsAdmin.php | 12 +- .../libraries/RainLoop/Plugins/Property.php | 12 +- .../RainLoop/Plugins/PropertyCollection.php | 4 +- .../libraries/RainLoop/Providers/Domain.php | 4 +- .../app/libraries/RainLoop/ServiceActions.php | 9 +- .../Admin/AdminSettingsPluginProperty.html | 28 +-- .../templates/Views/Admin/PopupsPlugin.html | 6 +- .../templates/Views/User/PopupsAccount.html | 6 +- 45 files changed, 361 insertions(+), 412 deletions(-) diff --git a/dev/App/User.js b/dev/App/User.js index bc40fe6b0..1adfdf142 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -79,7 +79,7 @@ export class AppUser extends AbstractApp { (currentTime > (lastTime + interval + 1000)) && Remote.request('Version', iError => (100 < iError) && location.reload(), - { Version: Settings.app('version') } + { version: Settings.app('version') } ); lastTime = currentTime; }, interval); diff --git a/dev/Common/Folders.js b/dev/Common/Folders.js index 9c99f95db..1c31720d6 100644 --- a/dev/Common/Folders.js +++ b/dev/Common/Folders.js @@ -198,7 +198,7 @@ folderInformationMultiply = (boot = false) => { boot && setTimeout(() => folderInformationMultiply(true), 2000); } }, { - Folders: folders + folders: folders }); } }, @@ -226,11 +226,11 @@ messagesMoveHelper = (fromFolderFullName, toFolderFullName, uidsForMove) => { Remote.abort('MessageList').request('MessageMove', moveOrDeleteResponseHelper, { - FromFolder: fromFolderFullName, - ToFolder: toFolderFullName, - Uids: [...uidsForMove].join(','), - MarkAsRead: (isSpam || FolderUserStore.trashFolder() === toFolderFullName) ? 1 : 0, - Learning: isSpam ? 'SPAM' : isHam ? 'HAM' : '' + fromFolder: fromFolderFullName, + toFolder: toFolderFullName, + uids: [...uidsForMove].join(','), + markAsRead: (isSpam || FolderUserStore.trashFolder() === toFolderFullName) ? 1 : 0, + learning: isSpam ? 'SPAM' : isHam ? 'HAM' : '' } ); }, @@ -240,7 +240,7 @@ messagesDeleteHelper = (sFromFolderFullName, aUidForRemove) => { moveOrDeleteResponseHelper, { folder: sFromFolderFullName, - Uids: [...aUidForRemove].join(',') + uids: [...aUidForRemove].join(',') } ); }, @@ -259,9 +259,9 @@ moveMessagesToFolder = (sFromFolderFullName, oUids, sToFolderFullName, bCopy) => if (oFromFolder && oToFolder) { bCopy ? Remote.request('MessageCopy', null, { - FromFolder: oFromFolder.fullName, - ToFolder: oToFolder.fullName, - Uids: [...oUids].join(',') + fromFolder: oFromFolder.fullName, + toFolder: oToFolder.fullName, + uids: [...oUids].join(',') }) : messagesMoveHelper(oFromFolder.fullName, oToFolder.fullName, oUids); @@ -283,7 +283,7 @@ dropFilesInFolder = (sFolderFullName, files) => { ++count; let data = new FormData; data.append('folder', sFolderFullName); - data.append('AppendFile', file); + data.append('appendFile', file); data.XToken = Settings.app('token'); fetch(serverRequest('Append'), { method: 'POST', diff --git a/dev/Common/UtilsUser.js b/dev/Common/UtilsUser.js index d71fa0bbd..41809b6c4 100644 --- a/dev/Common/UtilsUser.js +++ b/dev/Common/UtilsUser.js @@ -12,6 +12,7 @@ import { SettingsUserStore } from 'Stores/User/Settings'; import * as Local from 'Storage/Client'; import { ThemeStore } from 'Stores/Theme'; import Remote from 'Remote/User/Fetch'; +import { attachmentDownload } from 'Common/Links'; export const @@ -42,6 +43,28 @@ download = (link, name = "") => { } }, +downloadZip = (hashes, onError, fTrigger, folder) => { + if (hashes.length) { + let params = { + target: 'zip', + hashes: hashes + }; + if (!onError) { + onError = () => alert('Download failed'); + } + if (folder) { + params.folder = folder; +// params.uids = uids; + } + Remote.post('AttachmentsActions', fTrigger || null, params) + .then(result => { + let hash = result?.Result?.fileHash; + hash ? download(attachmentDownload(hash), hash+'.zip') : onError(); + }) + .catch(onError); + } +}, + /** * @returns {function} */ diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index f78f4bfdb..e94de8ff4 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -301,8 +301,8 @@ export class FolderModel extends AbstractModel { this.exists = true; this.hash = ''; -// this.id = null; - this.uidNext = null; + this.id = 0; + this.uidNext = 0; addObservablesTo(this, { name: '', diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 036d7b0b4..5c940ec05 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -44,9 +44,9 @@ const } }, { folder: message.folder, - Uids: message.uid, - Keyword: keyword, - SetAction: isSet ? 0 : 1 + uids: message.uid, + keyword: keyword, + setAction: isSet ? 0 : 1 }) }, diff --git a/dev/Settings/Admin/About.js b/dev/Settings/Admin/About.js index b5f8c35ff..03801fce5 100644 --- a/dev/Settings/Admin/About.js +++ b/dev/Settings/Admin/About.js @@ -59,10 +59,10 @@ export class AdminSettingsAbout /*extends AbstractViewSettings*/ { this.coreChecking(false); if (!iError && data?.Result) { this.coreReal(true); - this.coreUpdatable(!!data.Result.Updatable); - this.coreWarning(!!data.Result.Warning); - this.coreVersion(data.Result.Version || ''); - this.coreVersionCompare(data.Result.VersionCompare); + this.coreUpdatable(!!data.Result.updatable); + this.coreWarning(!!data.Result.warning); + this.coreVersion(data.Result.version || ''); + this.coreVersionCompare(data.Result.versionCompare); } else { this.coreReal(false); this.coreWarning(false); diff --git a/dev/Settings/Admin/Domains.js b/dev/Settings/Admin/Domains.js index 881854999..80fc9bff4 100644 --- a/dev/Settings/Admin/Domains.js +++ b/dev/Settings/Admin/Domains.js @@ -25,7 +25,7 @@ export class AdminSettingsDomains /*extends AbstractViewSettings*/ { } }, { - username: this.username() + username: this.username } ); } @@ -41,15 +41,15 @@ export class AdminSettingsDomains /*extends AbstractViewSettings*/ { deleteDomain(domain) { DomainAdminStore.remove(domain); Remote.request('AdminDomainDelete', DomainAdminStore.fetch, { - Name: domain.name + name: domain.name }); } disableDomain(domain) { domain.disabled(!domain.disabled()); Remote.request('AdminDomainDisable', DomainAdminStore.fetch, { - Name: domain.name, - Disabled: domain.disabled() ? 1 : 0 + name: domain.name, + disabled: domain.disabled() ? 1 : 0 }); } @@ -59,7 +59,7 @@ export class AdminSettingsDomains /*extends AbstractViewSettings*/ { el && ko.dataFor(el) && Remote.request('AdminDomainLoad', (iError, oData) => iError || showScreenPopup(DomainPopupView, [oData.Result]), { - Name: ko.dataFor(el).name + name: ko.dataFor(el).name } ); diff --git a/dev/Settings/Admin/Packages.js b/dev/Settings/Admin/Packages.js index fb0191987..3a0925880 100644 --- a/dev/Settings/Admin/Packages.js +++ b/dev/Settings/Admin/Packages.js @@ -46,7 +46,7 @@ export class AdminSettingsPackages extends AbstractViewSettings { data && Remote.request('AdminPluginLoad', (iError, data) => iError || showScreenPopup(PluginPopupView, [data.Result]), { - Id: data.id + id: data.id } ); // disablePlugin @@ -84,7 +84,7 @@ export class AdminSettingsPackages extends AbstractViewSettings { Remote.request('AdminPackageDelete', this.requestHelper(packageToDelete, false), { - Id: packageToDelete.id + id: packageToDelete.id } ); } @@ -96,9 +96,9 @@ export class AdminSettingsPackages extends AbstractViewSettings { Remote.request('AdminPackageInstall', this.requestHelper(packageToInstall, true), { - Id: packageToInstall.id, - Type: packageToInstall.type, - File: packageToInstall.file + id: packageToInstall.id, + type: packageToInstall.type, + file: packageToInstall.file }, 60000 ); @@ -120,8 +120,8 @@ export class AdminSettingsPackages extends AbstractViewSettings { } // PackageAdminStore.fetch(); }, { - Id: plugin.id, - Disabled: disable ? 1 : 0 + id: plugin.id, + disabled: disable ? 1 : 0 } ); } diff --git a/dev/Settings/Admin/Security.js b/dev/Settings/Admin/Security.js index 3dbfd37fa..3b99c16ab 100644 --- a/dev/Settings/Admin/Security.js +++ b/dev/Settings/Admin/Security.js @@ -102,10 +102,10 @@ export class AdminSettingsSecurity extends AbstractViewSettings { this.weakPassword(!!data.Result.Weak); } }, { - 'Login': this.adminLogin(), - 'Password': this.adminPassword(), - 'NewPassword': this.adminPasswordNew(), - 'TOTP': this.adminTOTP() + Login: this.adminLogin(), + Password: this.adminPassword(), + newPassword: this.adminPasswordNew(), + TOTP: this.adminTOTP() }); return true; diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index 287a3cd80..1e5694dad 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -67,7 +67,7 @@ export class UserSettingsAccounts /*extends AbstractViewSettings*/ { rl.app.accountsAndIdentities(); } }, { - EmailToDelete: accountToRemove.email + emailToDelete: accountToRemove.email }); } } @@ -81,7 +81,7 @@ export class UserSettingsAccounts /*extends AbstractViewSettings*/ { this.identityForDeletion(null); IdentityUserStore.remove(oIdentity => identityToRemove === oIdentity); Remote.request('IdentityDelete', () => rl.app.accountsAndIdentities(), { - IdToDelete: identityToRemove.id() + idToDelete: identityToRemove.id() }); } } diff --git a/dev/Settings/User/Folders.js b/dev/Settings/User/Folders.js index c3dd8ec37..b16443d73 100644 --- a/dev/Settings/User/Folders.js +++ b/dev/Settings/User/Folders.js @@ -160,8 +160,8 @@ export class UserSettingsFolders /*extends AbstractViewSettings*/ { // TODO: append '.default' ? Remote.request('FolderSetMetadata', null, { folder: folder.fullName, - Key: FolderMetadataKeys.KolabFolderType, - Value: type + key: FolderMetadataKeys.KolabFolderType, + value: type }); folder.kolabType(type); } diff --git a/dev/Stores/Admin/Domain.js b/dev/Stores/Admin/Domain.js index 195ff2b77..abd599491 100644 --- a/dev/Stores/Admin/Domain.js +++ b/dev/Stores/Admin/Domain.js @@ -20,6 +20,6 @@ DomainAdminStore.fetch = () => { ); } }, { - IncludeAliases: 1 + includeAliases: 1 }); }; diff --git a/dev/Stores/User/GnuPG.js b/dev/Stores/User/GnuPG.js index 48dd52630..c312e7cde 100644 --- a/dev/Stores/User/GnuPG.js +++ b/dev/Stores/User/GnuPG.js @@ -79,7 +79,7 @@ export const GnuPGUserStore = new class { }, { keyId: key.id, isPrivate: isPrivate, - Passphrase: pass + passphrase: pass } ); if (isPrivate) { @@ -115,7 +115,7 @@ export const GnuPGUserStore = new class { } callback?.(iError, oData); }, { - Key: key + key: key } ); } @@ -181,10 +181,10 @@ export const GnuPGUserStore = new class { uid: message.uid, partId: pgpInfo.PartId, keyId: key.id, - Passphrase: await askPassphrase(key, 'BUTTON_DECRYPT'), - Data: '' // message.plain() optional + passphrase: await askPassphrase(key, 'BUTTON_DECRYPT'), + data: '' // message.plain() optional } - if (null !== params.Passphrase) { + if (null !== params.passphrase) { const result = await Remote.post('GnupgDecrypt', null, params); if (result?.Result && false !== result.Result.data) { return result.Result; @@ -202,9 +202,9 @@ export const GnuPGUserStore = new class { // let mode = await this.hasPublicKeyForEmails([sender]); data.folder = message.folder; data.uid = message.uid; - if (data.BodyPart) { - data.BodyPart = data.BodyPart.raw; - data.SigPart = data.SigPart.body; + if (data.bodyPart) { + data.bodyPart = data.bodyPart.raw; + data.sigPart = data.sigPart.body; } let response = await Remote.post('MessagePgpVerify', null, data); if (response?.Result) { diff --git a/dev/Stores/User/Messagelist.js b/dev/Stores/User/Messagelist.js index 6066a690b..b7a075ad6 100644 --- a/dev/Stores/User/Messagelist.js +++ b/dev/Stores/User/Messagelist.js @@ -331,8 +331,8 @@ MessagelistUserStore.setAction = (sFolderFullName, iSetAction, messages) => { } Remote.request('MessageSetSeen', null, { folder: sFolderFullName, - Uids: rootUids.join(','), - SetAction: iSetAction == MessageSetAction.SetSeen ? 1 : 0 + uids: rootUids.join(','), + setAction: iSetAction == MessageSetAction.SetSeen ? 1 : 0 }); break; @@ -340,8 +340,8 @@ MessagelistUserStore.setAction = (sFolderFullName, iSetAction, messages) => { case MessageSetAction.UnsetFlag: Remote.request('MessageSetFlagged', null, { folder: sFolderFullName, - Uids: rootUids.join(','), - SetAction: iSetAction == MessageSetAction.SetFlag ? 1 : 0 + uids: rootUids.join(','), + setAction: iSetAction == MessageSetAction.SetFlag ? 1 : 0 }); break; // no default diff --git a/dev/Stores/User/OpenPGP.js b/dev/Stores/User/OpenPGP.js index a3defe280..682db3d8c 100644 --- a/dev/Stores/User/OpenPGP.js +++ b/dev/Stores/User/OpenPGP.js @@ -219,12 +219,13 @@ export const OpenPGPUserStore = new class { if (data && publicKey) { data.folder = message.folder; data.uid = message.uid; - data.GnuPG = 0; + data.tryGnuPG = 0; let response; if (data.sigPartId) { response = await Remote.post('MessagePgpVerify', null, data); - } else if (data.BodyPart) { - response = { Result: { text: data.BodyPart.raw, signature: data.SigPart.body } }; + } else if (data.bodyPart) { + // MimePart + response = { Result: { text: data.bodyPart.raw, signature: data.sigPart.body } }; } else { response = { Result: { text: message.plain(), signature: null } }; } diff --git a/dev/View/Popup/Account.js b/dev/View/Popup/Account.js index fd58bb680..02ab08e29 100644 --- a/dev/View/Popup/Account.js +++ b/dev/View/Popup/Account.js @@ -29,7 +29,7 @@ export class AccountPopupView extends AbstractViewPopup { submitForm(form) { if (!this.submitRequest() && form.reportValidity()) { const data = new FormData(form); - data.set('New', this.isNew() ? 1 : 0); + data.set('new', this.isNew() ? 1 : 0); this.submitRequest(true); Remote.request('AccountSetup', (iError, data) => { this.submitRequest(false); diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 246c6bcbd..123ad13a7 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -1482,7 +1482,7 @@ export class ComposePopupView extends AbstractViewPopup { signature.body = await OpenPGPUserStore.sign(data.toString(), sign[1], 1); signed.children.push(signature); params.signed = signed.toString(); - params.Boundary = signed.boundary; + params.boundary = signed.boundary; data = signed; } else if ('gnupg' == sign[0]) { // TODO: sign in PHP fails diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 2bc418e67..27d55fb22 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -104,10 +104,10 @@ export class ContactsPopupView extends AbstractViewPopup { const contacts = this.contactsCheckedOrSelected(); if (contacts.length) { let selectorContact = this.selectorContact(), - Uids = [], + uids = [], count = 0; contacts.forEach(contact => { - Uids.push(contact.id()); + uids.push(contact.id()); if (selectorContact && selectorContact.id() === contact.id()) { this.selectorContact(selectorContact = null); } @@ -127,7 +127,7 @@ export class ContactsPopupView extends AbstractViewPopup { } this.reloadContactList(); }, { - Uids: Uids.join(',') + uids: uids.join(',') } ); } diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index 7babe05b8..d2535f8b2 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -14,13 +14,64 @@ import { AskPopupView } from 'View/Popup/Ask'; const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1), + domainDefaults = { + enableSmartPorts: false, + + savingError: '', + + name: '', + + imapHost: '', + imapPort: 143, + imapType: 0, + imapTimeout: 300, + imapShortLogin: false, + // SSL + imapSslVerify_peer: false, + imapSslAllow_self_signed: false, + // Options + imapDisable_list_status: false, + imapDisable_metadata: false, + imapDisable_move: false, + imapDisable_sort: false, + imapDisable_thread: false, + imapExpunge_all_on_delete: false, + imapFast_simple_search: true, + imapFetch_new_messages: true, + imapForce_select: false, + imapFolder_list_limit: 200, + imapMessage_all_headers: false, + imapMessage_list_limit: 0, + imapSearch_filter: '', + + sieveEnabled: false, + sieveHost: '', + sievePort: 4190, + sieveType: 0, + sieveTimeout: 10, + + smtpHost: '', + smtpPort: 25, + smtpType: 0, + smtpTimeout: 60, + smtpShortLogin: false, + smtpUseAuth: true, + smtpSetSender: false, + smtpUsePhpMail: false, + // SSL + smtpSslVerify_peer: false, + smtpSslAllow_self_signed: false, + + whiteList: '', + aliasName: '' + }, domainToParams = oDomain => ({ - Name: oDomain.name(), + name: oDomain.name, IMAP: { - host: oDomain.imapHost(), - port: oDomain.imapPort(), + host: oDomain.imapHost, + port: oDomain.imapPort, secure: pInt(oDomain.imapType()), - timeout: oDomain.imapTimeout(), + timeout: oDomain.imapTimeout, shortLogin: !!oDomain.imapShortLogin(), ssl: { verify_peer: !!oDomain.imapSslVerify_peer(), @@ -44,10 +95,10 @@ const */ }, SMTP: { - host: oDomain.smtpHost(), - port: oDomain.smtpPort(), + host: oDomain.smtpHost, + port: oDomain.smtpPort, secure: pInt(oDomain.smtpType()), - timeout: oDomain.smtpTimeout(), + timeout: oDomain.smtpTimeout, shortLogin: !!oDomain.smtpShortLogin(), ssl: { verify_peer: !!oDomain.smtpSslVerify_peer(), @@ -60,10 +111,10 @@ const }, Sieve: { enabled: !!oDomain.sieveEnabled(), - host: oDomain.sieveHost(), - port: oDomain.sievePort(), + host: oDomain.sieveHost, + port: oDomain.sievePort, secure: pInt(oDomain.sieveType()), - timeout: oDomain.sieveTimeout(), + timeout: oDomain.sieveTimeout, shortLogin: !!oDomain.imapShortLogin(), ssl: { verify_peer: !!oDomain.imapSslVerify_peer(), @@ -71,14 +122,14 @@ const allow_self_signed: !!oDomain.imapSslAllow_self_signed() } }, - whiteList: oDomain.whiteList() + whiteList: oDomain.whiteList }); export class DomainPopupView extends AbstractViewPopup { constructor() { super('Domain'); - addObservablesTo(this, this.getDefaults()); + addObservablesTo(this, domainDefaults); addObservablesTo(this, { edit: false, @@ -99,21 +150,11 @@ export class DomainPopupView extends AbstractViewPopup { headerText: () => { const name = this.name(), aliasName = this.aliasName(); - - let result = ''; - - if (this.edit()) { - result = i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { NAME: name }); - if (aliasName) { - result += ' ⫘ ' + aliasName; - } - } else { - result = name - ? i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN_WITH_NAME', { NAME: name }) - : i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN'); - } - - return result; + return this.edit() + ? i18n('POPUPS_DOMAIN/TITLE_EDIT_DOMAIN', { NAME: name }) + (aliasName ? ' ⫘ ' + aliasName : '') + : (name + ? i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN_WITH_NAME', { NAME: name }) + : i18n('POPUPS_DOMAIN/TITLE_ADD_DOMAIN')); }, domainDesc: () => { @@ -213,7 +254,7 @@ export class DomainPopupView extends AbstractViewPopup { } }, Object.assign(domainToParams(this), { - Create: this.edit() ? 0 : 1 + create: this.edit() ? 0 : 1 }) ); } @@ -260,7 +301,9 @@ export class DomainPopupView extends AbstractViewPopup { onShow(oDomain) { this.saving(false); this.clearTesting(); - this.clearForm(); + this.edit(false); + forEachObjectEntry(domainDefaults, (key, value) => this[key](value)); + this.enableSmartPorts(true); if (oDomain) { this.enableSmartPorts(false); this.edit(true); @@ -284,64 +327,4 @@ export class DomainPopupView extends AbstractViewPopup { this.enableSmartPorts(true); } } - - getDefaults() { - return { - enableSmartPorts: false, - - savingError: '', - - name: '', - - imapHost: '', - imapPort: 143, - imapType: 0, - imapTimeout: 300, - imapShortLogin: false, - // SSL - imapSslVerify_peer: false, - imapSslAllow_self_signed: false, - // Options - imapDisable_list_status: false, - imapDisable_metadata: false, - imapDisable_move: false, - imapDisable_sort: false, - imapDisable_thread: false, - imapExpunge_all_on_delete: false, - imapFast_simple_search: true, - imapFetch_new_messages: true, - imapForce_select: false, - imapFolder_list_limit: 200, - imapMessage_all_headers: false, - imapMessage_list_limit: 0, - imapSearch_filter: '', - - sieveEnabled: false, - sieveHost: '', - sievePort: 4190, - sieveType: 0, - sieveTimeout: 10, - - smtpHost: '', - smtpPort: 25, - smtpType: 0, - smtpTimeout: 60, - smtpShortLogin: false, - smtpUseAuth: true, - smtpSetSender: false, - smtpUsePhpMail: false, - // SSL - smtpSslVerify_peer: false, - smtpSslAllow_self_signed: false, - - whiteList: '', - aliasName: '' - }; - } - - clearForm() { - this.edit(false); - forEachObjectEntry(this.getDefaults(), (key, value) => this[key](value)); - this.enableSmartPorts(true); - } } diff --git a/dev/View/Popup/DomainAlias.js b/dev/View/Popup/DomainAlias.js index 50cbd038c..30cee4515 100644 --- a/dev/View/Popup/DomainAlias.js +++ b/dev/View/Popup/DomainAlias.js @@ -46,21 +46,15 @@ export class DomainAliasPopupView extends AbstractViewPopup { this.close(); } }, { - Name: this.name(), - Alias: this.alias() + name: this.name, + alias: this.alias }); } onShow() { - this.clearForm(); - } - - clearForm() { this.saving(false); this.savingError(''); - this.name(''); - this.alias(''); } } diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index 0e482c6e0..4761cc72b 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -41,19 +41,19 @@ export class PluginPopupView extends AbstractViewPopup { saveCommand() { const oConfig = { - Id: this.id(), - Settings: {} + id: this.id, + settings: {} }, setItem = item => { let value = item.value(); if (false === value || true === value) { value = value ? 1 : 0; } - oConfig.Settings[item.Name] = value; + oConfig.settings[item.name] = value; }; this.config.forEach(oItem => { - if (7 == oItem.Type) { + if (7 == oItem.type) { // Group oItem.config.forEach(oSubItem => setItem(oSubItem)); } else { @@ -76,15 +76,15 @@ export class PluginPopupView extends AbstractViewPopup { this.config([]); if (oPlugin) { - this.id(oPlugin.Id); - this.name(oPlugin.Name); - this.readme(oPlugin.Readme); + this.id(oPlugin.id); + this.name(oPlugin.name); + this.readme(oPlugin.readme); - const config = oPlugin.Config; + const config = oPlugin.config; if (arrayLength(config)) { this.config( config.map(item => { - if (7 == item.Type) { + if (7 == item.type) { // Group item.config.forEach(subItem => { subItem.value = ko.observable(subItem.value); diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index 1c5f66323..fd89f66a5 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -13,11 +13,11 @@ import { doc, } from 'Common/Globals'; import { arrayLength } from 'Common/Utils'; -import { computedPaginatorHelper, showMessageComposer, populateMessageBody, download, moveAction } from 'Common/UtilsUser'; +import { computedPaginatorHelper, showMessageComposer, populateMessageBody, downloadZip, moveAction } from 'Common/UtilsUser'; import { FileInfo } from 'Common/File'; import { isFullscreen, toggleFullscreen } from 'Common/Fullscreen'; -import { mailBox, attachmentDownload } from 'Common/Links'; +import { mailBox } from 'Common/Links'; import { Selector } from 'Common/Selector'; import { i18n } from 'Common/Translator'; @@ -367,23 +367,7 @@ export class MailMessageList extends AbstractViewRight { let hashes = []/*, uids = []*/; // MessagelistUserStore.forEach(message => message.checked() && uids.push(message.uid)); MessagelistUserStore.forEach(message => message.checked() && hashes.push(message.requestHash)); - if (hashes.length) { - Remote.post('AttachmentsActions', null, { - Do: 'Zip', - folder: MessagelistUserStore().folder, -// Uids: uids, - Hashes: hashes - }) - .then(result => { - let hash = result?.Result?.fileHash; - if (hash) { - download(attachmentDownload(hash), hash+'.zip'); - } else { - alert('Download failed'); - } - }) - .catch(() => alert('Download failed')); - } + downloadZip(hashes, null, null, MessagelistUserStore().folder); } downloadAttachCommand() { @@ -397,21 +381,7 @@ export class MailMessageList extends AbstractViewRight { }); } }); - 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')); - } + downloadZip(hashes); } deleteWithoutMoveCommand() { @@ -505,8 +475,8 @@ export class MailMessageList extends AbstractViewRight { Remote.request('MessageSetSeenToAll', null, { folder: sFolderFullName, - SetAction: 1, - ThreadUids: uids.join(',') + setAction: 1, + threadUids: uids.join(',') }); MessagelistUserStore.reloadFlagsAndCachedMessage(); diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 88f4ffde4..158040cb6 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -23,13 +23,12 @@ import { } from 'Common/Globals'; import { arrayLength } from 'Common/Utils'; -import { download, mailToHelper, showMessageComposer, moveAction } from 'Common/UtilsUser'; +import { download, downloadZip, mailToHelper, showMessageComposer, moveAction } from 'Common/UtilsUser'; import { isFullscreen, exitFullscreen, toggleFullscreen } from 'Common/Fullscreen'; import { SMAudio } from 'Common/Audio'; import { i18n } from 'Common/Translator'; -import { attachmentDownload } from 'Common/Links'; import { MessageFlagsCache } from 'Common/Cache'; @@ -486,21 +485,10 @@ export class MailMessageView extends AbstractViewRight { const hashes = (currentMessage()?.attachments || []) .map(item => item?.checked() /*&& !item?.isLinked()*/ ? item.download : '') .filter(v => v); - if (hashes.length) { - Remote.post('AttachmentsActions', this.downloadAsZipLoading, { - Do: 'Zip', - Hashes: hashes - }) - .then(result => { - let hash = result?.Result?.fileHash; - if (hash) { - download(attachmentDownload(hash), hash+'.zip'); - } else { - this.downloadAsZipError(true); - } - }) - .catch(() => this.downloadAsZipError(true)); - } + downloadZip(hashes, + () => this.downloadAsZipError(true), + this.downloadAsZipLoading + ); } /** diff --git a/plugins/README.md b/plugins/README.md index 28ce1583a..9774ec445 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -455,11 +455,18 @@ Converts HTML to text Converts text to HTML ## rl.addSettingsViewModel(SettingsViewModelClass, template, labelName, route)​ +Examples in +* ./change-password/js/ChangePasswordUserSettings.js +* ./example/js/ExampleUserSettings.js +* ./kolab/js/settings.js +* ./two-factor-auth/js/TwoFactorAuthSettings.js ## rl.addSettingsViewModelForAdmin(SettingsViewModelClass, template, labelName, route)​ +Examples in +* ./example/js/ExampleAdminSettings.js:34: rl.addSettingsViewModelForAdmin(ExampleAdminSettings, 'ExampleAdminSettingsTab', ## rl.adminArea()​ -Returns true or false +Returns true or false when in '?admin' area ## rl.app.Remote.abort(action)​​​​​ diff --git a/plugins/nextcloud/js/message.js b/plugins/nextcloud/js/message.js index 922fa8781..f5154b745 100644 --- a/plugins/nextcloud/js/message.js +++ b/plugins/nextcloud/js/message.js @@ -63,8 +63,8 @@ if (folder) { rl.fetchJSON('./?/Json/&q[]=/0/', {}, { Action: 'AttachmentsActions', - Do: 'nextcloud', - Hashes: hashes, + target: 'nextcloud', + hashes: hashes, NcFolder: folder }) .then(result => { diff --git a/plugins/nextcloud/js/messagelist.js b/plugins/nextcloud/js/messagelist.js index 5b5d27b43..8b8681afa 100644 --- a/plugins/nextcloud/js/messagelist.js +++ b/plugins/nextcloud/js/messagelist.js @@ -28,9 +28,8 @@ } }); - let template = document.getElementById('MailMessageList'); - - const msgMenu = template.content.querySelector('#more-list-dropdown-id + menu [data-bind*="forwardCommand"]'); + const msgMenu = document.getElementById('MailMessageList') + .content.querySelector('#more-list-dropdown-id + menu [data-bind*="forwardCommand"]'); if (msgMenu) { msgMenu.after(Element.fromHTML(`
          • 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 8e850c945..c340dc25c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -926,7 +926,7 @@ class Actions public function DoVersion(): array { - return $this->DefaultResponse(APP_VERSION === (string)$this->GetActionParam('Version', '')); + return $this->DefaultResponse(APP_VERSION === (string)$this->GetActionParam('version', '')); } public function MainClearFileName(string $sFileName, string $sContentType, string $sMimeIndex, int $iMaxLength = 250): string @@ -988,15 +988,12 @@ class Actions return $sError; } - public function Upload(): array + public function Upload(?array $aFile, int $iError): array { $oAccount = $this->getAccountFromToken(); $aResponse = array(); - $aFile = $this->GetActionParam('File', null); - $iError = $this->GetActionParam('Error', Enumerations\UploadError::UNKNOWN); - if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) { $sSavedName = 'upload-post-' . \md5($aFile['name'] . $aFile['tmp_name']); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index 91dc8ae02..2e5d8e639 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -80,10 +80,10 @@ trait Accounts $aAccounts = $this->GetAccounts($oMainAccount); - $sEmail = \trim($this->GetActionParam('Email', '')); - $sPassword = $this->GetActionParam('Password', ''); - $sName = $this->GetActionParam('Name', ''); - $bNew = '1' === (string)$this->GetActionParam('New', '1'); + $sEmail = \trim($this->GetActionParam('email', '')); + $sPassword = $this->GetActionParam('password', ''); + $sName = \trim($this->GetActionParam('name', '')); + $bNew = !empty($this->GetActionParam('new', 1)); $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); if ($bNew && ($oMainAccount->Email() === $sEmail || isset($aAccounts[$sEmail]))) { @@ -172,7 +172,7 @@ trait Accounts return $this->FalseResponse(); } - $sEmailToDelete = \trim($this->GetActionParam('EmailToDelete', '')); + $sEmailToDelete = \trim($this->GetActionParam('emailToDelete', '')); $sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true); $aAccounts = $this->GetAccounts($oMainAccount); @@ -255,7 +255,7 @@ trait Accounts return $this->FalseResponse(); } - $sId = \trim($this->GetActionParam('IdToDelete', '')); + $sId = \trim($this->GetActionParam('idToDelete', '')); if (empty($sId)) { throw new ClientException(Notifications::UnknownError); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php index 5836504eb..70e5b0554 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminDomains.php @@ -11,13 +11,13 @@ trait AdminDomains { $this->IsAdminLoggined(); - return $this->DefaultResponse($this->DomainProvider()->Load($this->GetActionParam('Name', ''), false, false)); + return $this->DefaultResponse($this->DomainProvider()->Load($this->GetActionParam('name', ''), false, false)); } public function DoAdminDomainList() : array { $this->IsAdminLoggined(); - $bIncludeAliases = !empty($this->GetActionParam('IncludeAliases', '1')); + $bIncludeAliases = !empty($this->GetActionParam('includeAliases', '1')); return $this->DefaultResponse($this->DomainProvider()->GetList($bIncludeAliases)); } @@ -25,7 +25,7 @@ trait AdminDomains { $this->IsAdminLoggined(); - return $this->DefaultResponse($this->DomainProvider()->Delete((string) $this->GetActionParam('Name', ''))); + return $this->DefaultResponse($this->DomainProvider()->Delete((string) $this->GetActionParam('name', ''))); } public function DoAdminDomainDisable() : array @@ -33,8 +33,8 @@ trait AdminDomains $this->IsAdminLoggined(); return $this->DefaultResponse($this->DomainProvider()->Disable( - (string) $this->GetActionParam('Name', ''), - '1' === (string) $this->GetActionParam('Disabled', '0') + (string) $this->GetActionParam('name', ''), + !empty($this->GetActionParam('disabled', '0')) )); } @@ -52,8 +52,8 @@ trait AdminDomains $this->IsAdminLoggined(); return $this->DefaultResponse($this->DomainProvider()->SaveAlias( - (string) $this->GetActionParam('Name', ''), - (string) $this->GetActionParam('Alias', '') + (string) $this->GetActionParam('name', ''), + (string) $this->GetActionParam('alias', '') )); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php index 4fb554fdb..f142e7f22 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/AdminExtensions.php @@ -16,7 +16,7 @@ trait AdminExtensions public function DoAdminPackageDelete() : array { - $sId = $this->GetActionParam('Id', ''); + $sId = $this->GetActionParam('id', ''); $bResult = \SnappyMail\Repository::deletePackage($sId); static::pluginEnable($sId, false); return $this->DefaultResponse($bResult); @@ -24,11 +24,11 @@ trait AdminExtensions public function DoAdminPackageInstall() : array { - $sType = $this->GetActionParam('Type', ''); + $sType = $this->GetActionParam('type', ''); $bResult = \SnappyMail\Repository::installPackage( $sType, - $this->GetActionParam('Id', ''), - $this->GetActionParam('File', '') + $this->GetActionParam('id', ''), + $this->GetActionParam('file', '') ); return $this->DefaultResponse($bResult ? ('plugin' !== $sType ? array('Reload' => true) : true) : false); @@ -38,8 +38,8 @@ trait AdminExtensions { $this->IsAdminLoggined(); - $sId = (string) $this->GetActionParam('Id', ''); - $bDisable = '1' === (string) $this->GetActionParam('Disabled', '1'); + $sId = (string) $this->GetActionParam('id', ''); + $bDisable = '1' === (string) $this->GetActionParam('disabled', '1'); if (!$bDisable) { $oPlugin = $this->Plugins()->CreatePluginByName($sId); @@ -61,17 +61,17 @@ trait AdminExtensions $this->IsAdminLoggined(); $mResult = false; - $sId = (string) $this->GetActionParam('Id', ''); + $sId = (string) $this->GetActionParam('id', ''); if (!empty($sId)) { $oPlugin = $this->Plugins()->CreatePluginByName($sId); if ($oPlugin) { $mResult = array( '@Object' => 'Object/Plugin', - 'Id' => $sId, - 'Name' => $oPlugin->Name(), - 'Readme' => $oPlugin->Description(), - 'Config' => array() + 'id' => $sId, + 'name' => $oPlugin->Name(), + 'readme' => $oPlugin->Description(), + 'config' => array() ); $aMap = $oPlugin->ConfigMap(); @@ -85,7 +85,7 @@ trait AdminExtensions } else { $oItem->SetValue($oConfig->Get('plugin', $oItem->Name(), '')); } - $mResult['Config'][] = $oItem; + $mResult['config'][] = $oItem; } else if ($oItem instanceof \RainLoop\Plugins\PropertyCollection) { foreach ($oItem as $oSubItem) { if ($oSubItem && $oSubItem instanceof \RainLoop\Plugins\Property) { @@ -96,7 +96,7 @@ trait AdminExtensions } } } - $mResult['Config'][] = $oItem; + $mResult['config'][] = $oItem; } } } @@ -111,7 +111,7 @@ trait AdminExtensions { $this->IsAdminLoggined(); - $sId = (string) $this->GetActionParam('Id', ''); + $sId = (string) $this->GetActionParam('id', ''); if (!empty($sId)) { $oPlugin = $this->Plugins()->CreatePluginByName($sId); @@ -119,7 +119,7 @@ trait AdminExtensions $oConfig = $oPlugin->Config(); $aMap = $oPlugin->ConfigMap(true); if (\is_array($aMap)) { - $aSettings = (array) $this->GetActionParam('Settings', []); + $aSettings = (array) $this->GetActionParam('settings', []); foreach ($aMap as $oItem) { $sKey = $oItem->Name(); $sValue = $aSettings[$sKey] ?? $oConfig->Get('plugin', $sKey); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php index f16ffabb1..cbfb1ec5a 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Attachments.php @@ -12,9 +12,9 @@ trait Attachments */ public function DoAttachmentsActions() : array { - $sAction = $this->GetActionParam('Do', ''); + $sAction = $this->GetActionParam('target', ''); $sFolder = $this->GetActionParam('folder', ''); - $aHashes = $this->GetActionParam('Hashes', null); + $aHashes = $this->GetActionParam('hashes', null); $oFilesProvider = $this->FilesProvider(); if (empty($sAction) || !$this->GetCapa(Capa::ATTACHMENTS_ACTIONS) || !$oFilesProvider || !$oFilesProvider->IsActive()) { return $this->FalseResponse(); 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 307f618ef..6d6de7c56 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 @@ -77,7 +77,7 @@ trait Contacts public function DoContactsDelete() : array { $oAccount = $this->getAccountFromToken(); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + $aUids = \explode(',', (string) $this->GetActionParam('uids', '')); $aFilteredUids = \array_filter(\array_map('intval', $aUids)); @@ -119,15 +119,12 @@ trait Contacts )); } - public function UploadContacts() : array + public function UploadContacts(?array $aFile, int $iError) : array { $oAccount = $this->getAccountFromToken(); $mResponse = false; - $aFile = $this->GetActionParam('File', null); - $iError = $this->GetActionParam('Error', \RainLoop\Enumerations\UploadError::UNKNOWN); - if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) { $sSavedName = 'upload-post-'.\md5($aFile['name'].$aFile['tmp_name']); if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name'])) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php index 0ef61472d..ae3349deb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -22,13 +22,13 @@ trait Folders if ($oAccount && !empty($sFolderFullName) - && !empty($_FILES['AppendFile']) - && \is_uploaded_file($_FILES['AppendFile']['tmp_name']) - && \UPLOAD_ERR_OK == $_FILES['AppendFile']['error'] + && !empty($_FILES['appendFile']) + && \is_uploaded_file($_FILES['appendFile']['tmp_name']) + && \UPLOAD_ERR_OK == $_FILES['appendFile']['error'] && $this->oConfig->Get('labs', 'allow_message_append', false) ) { - $sSavedName = 'append-post-' . \md5($sFolderFullName . $_FILES['AppendFile']['name'] . $_FILES['AppendFile']['tmp_name']); - if ($this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $_FILES['AppendFile']['tmp_name'])) { + $sSavedName = 'append-post-' . \md5($sFolderFullName . $_FILES['appendFile']['name'] . $_FILES['appendFile']['tmp_name']); + if ($this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $_FILES['appendFile']['tmp_name'])) { $iMessageStreamSize = $this->FilesProvider()->FileSize($oAccount, $sSavedName); $rMessageStream = $this->FilesProvider()->GetFile($oAccount, $sSavedName); @@ -107,10 +107,10 @@ trait Folders { $this->initMailClientConnection(); $sFolderFullName = $this->GetActionParam('folder'); - $sMetadataKey = $this->GetActionParam('Key'); + $sMetadataKey = $this->GetActionParam('key'); if ($sFolderFullName && $sMetadataKey) { $this->MailClient()->FolderSetMetadata($sFolderFullName, [ - $sMetadataKey => $this->GetActionParam('Value') ?: null + $sMetadataKey => $this->GetActionParam('value') ?: null ]); } return $this->TrueResponse(); @@ -292,7 +292,7 @@ trait Folders { $aResult = array(); - $aFolders = $this->GetActionParam('Folders', null); + $aFolders = $this->GetActionParam('folders', null); if (\is_array($aFolders)) { $this->initMailClientConnection(); 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 2ee93bc4a..f2b176c92 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 @@ -369,7 +369,7 @@ trait Messages { $this->initMailClientConnection(); - $sThreadUids = \trim($this->GetActionParam('ThreadUids', '')); + $sThreadUids = \trim($this->GetActionParam('threadUids', '')); try { @@ -377,7 +377,7 @@ trait Messages $this->GetActionParam('folder', ''), empty($sThreadUids) ? new SequenceSet('1:*', false) : new SequenceSet(\explode(',', $sThreadUids)), MessageFlag::SEEN, - !empty($this->GetActionParam('SetAction', '0')) + !empty($this->GetActionParam('setAction', '0')) ); } catch (\Throwable $oException) @@ -395,7 +395,7 @@ trait Messages public function DoMessageSetKeyword() : array { - return $this->messageSetFlag($this->GetActionParam('Keyword', ''), true); + return $this->messageSetFlag($this->GetActionParam('keyword', ''), true); } /** @@ -447,7 +447,7 @@ trait Messages $this->initMailClientConnection(); $sFolder = $this->GetActionParam('folder', ''); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + $aUids = \explode(',', (string) $this->GetActionParam('uids', '')); try { @@ -478,12 +478,12 @@ trait Messages { $this->initMailClientConnection(); - $sFromFolder = $this->GetActionParam('FromFolder', ''); - $sToFolder = $this->GetActionParam('ToFolder', ''); + $sFromFolder = $this->GetActionParam('fromFolder', ''); + $sToFolder = $this->GetActionParam('toFolder', ''); - $oUids = new SequenceSet(\explode(',', (string) $this->GetActionParam('Uids', ''))); + $oUids = new SequenceSet(\explode(',', (string) $this->GetActionParam('uids', ''))); - if (!empty($this->GetActionParam('MarkAsRead', '0'))) { + if (!empty($this->GetActionParam('markAsRead', '0'))) { try { $this->MailClient()->MessageSetFlag($sFromFolder, $oUids, MessageFlag::SEEN); @@ -494,7 +494,7 @@ trait Messages } } - $sLearning = $this->GetActionParam('Learning', ''); + $sLearning = $this->GetActionParam('learning', ''); if ($sLearning) { try { @@ -544,9 +544,9 @@ trait Messages try { $this->MailClient()->MessageCopy( - $this->GetActionParam('FromFolder', ''), - $this->GetActionParam('ToFolder', ''), - new SequenceSet(\explode(',', (string) $this->GetActionParam('Uids', ''))) + $this->GetActionParam('fromFolder', ''), + $this->GetActionParam('toFolder', ''), + new SequenceSet(\explode(',', (string) $this->GetActionParam('uids', ''))) ); } catch (\Throwable $oException) @@ -688,8 +688,8 @@ trait Messages } } - // Try by default as OpenPGP.js sets GnuPG to 0 - if ($this->GetActionParam('GnuPG', 1)) { + // Try by default as OpenPGP.js sets useGnuPG to 0 + if ($this->GetActionParam('tryGnuPG', 1)) { $GPG = $this->GnuPG(); if ($GPG) { $info = $this->GnuPG()->verify($result['text'], $result['signature']); @@ -873,9 +873,9 @@ trait Messages { $this->MailClient()->MessageSetFlag( $this->GetActionParam('folder', ''), - new SequenceSet(\explode(',', (string) $this->GetActionParam('Uids', ''))), + new SequenceSet(\explode(',', (string) $this->GetActionParam('uids', ''))), $sMessageFlag, - !empty($this->GetActionParam('SetAction', '0')), + !empty($this->GetActionParam('setAction', '0')), $bSkipUnsupportedFlag ); } @@ -1041,7 +1041,7 @@ trait Messages if ($sSigned = $this->GetActionParam('signed', '')) { $aSigned = \explode("\r\n\r\n", $sSigned, 2); // $sBoundary = \preg_replace('/^.+boundary="([^"]+)".+$/Dsi', '$1', $aSigned[0]); - $sBoundary = $this->GetActionParam('Boundary', ''); + $sBoundary = $this->GetActionParam('boundary', ''); $oPart = new MimePart; $oPart->Headers->AddByName( @@ -1080,77 +1080,77 @@ trait Messages unset($oAlternativePart); unset($sEncrypted); + } else if ($sHtml = $this->GetActionParam('html', '')) { + $oPart = new MimePart; + $oPart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'multipart/alternative'); + $oMessage->SubParts->append($oPart); + + $sHtml = \MailSo\Base\HtmlUtils::BuildHtml($sHtml, $aFoundCids, $aFoundDataURL, $aFoundContentLocationUrls); + $this->Plugins()->RunHook('filter.message-html', array($oAccount, $oMessage, &$sHtml)); + + // First add plain + $sPlain = $this->GetActionParam('plain', '') ?: \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sHtml); + $this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain)); + $oAlternativePart = new MimePart; + $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' + ); + $oPart->SubParts->append($oAlternativePart); + unset($sPlain); + + // Now add HTML + $oAlternativePart = new MimePart; + $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' + ); + $oPart->SubParts->append($oAlternativePart); + + unset($oAlternativePart); + unset($sHtml); + } else { - if ($sHtml = $this->GetActionParam('html', '')) { + $sPlain = $this->GetActionParam('plain', ''); +/* + if ($sSignature = $this->GetActionParam('pgpSignature', null)) { $oPart = new MimePart; - $oPart->Headers->AddByName(MimeEnumHeader::CONTENT_TYPE, 'multipart/alternative'); + $oPart->Headers->AddByName( + MimeEnumHeader::CONTENT_TYPE, + 'multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"' + ); $oMessage->SubParts->append($oPart); - $sHtml = \MailSo\Base\HtmlUtils::BuildHtml($sHtml, $aFoundCids, $aFoundDataURL, $aFoundContentLocationUrls); - $this->Plugins()->RunHook('filter.message-html', array($oAccount, $oMessage, &$sHtml)); - - // First add plain - $sPlain = $this->GetActionParam('plain', '') ?: \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sHtml); - $this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain)); $oAlternativePart = new MimePart; - $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' - ); - $oPart->SubParts->append($oAlternativePart); - unset($sPlain); - - // Now add HTML - $oAlternativePart = new MimePart; - $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' - ); + $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); - unset($oAlternativePart); - unset($sHtml); + $oAlternativePart = new MimePart; + $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 { - $sPlain = $this->GetActionParam('plain', ''); - if ($sSignature = $this->GetActionParam('Signature', null)) { - $oPart = new MimePart; - $oPart->Headers->AddByName( - MimeEnumHeader::CONTENT_TYPE, - 'multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"' - ); - $oMessage->SubParts->append($oPart); - - $oAlternativePart = new MimePart; - $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(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(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' - ); - $oMessage->SubParts->append($oAlternativePart); - } - unset($oAlternativePart); unset($sSignature); - unset($sPlain); - } + } else { +*/ + $this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sPlain)); + $oAlternativePart = new MimePart; + $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' + ); + $oMessage->SubParts->append($oAlternativePart); + unset($oAlternativePart); + unset($sPlain); } unset($oPart); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php index c57dd9b16..030ef8aee 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php @@ -79,10 +79,10 @@ trait Pgp $GPG->addDecryptKey( $this->GetActionParam('keyId', ''), - $this->GetActionParam('Passphrase', '') + $this->GetActionParam('passphrase', '') ); - $sData = $this->GetActionParam('Data', ''); + $sData = $this->GetActionParam('data', ''); $oPart = null; $result = [ 'data' => '', @@ -127,7 +127,7 @@ trait Pgp $GPG = $this->GnuPG(); return $this->DefaultResponse($GPG ? $GPG->export( $this->GetActionParam('keyId', ''), - $this->GetActionParam('Passphrase', '') + $this->GetActionParam('passphrase', '') ) : false); } @@ -136,11 +136,11 @@ trait Pgp $fingerprint = false; $GPG = $this->GnuPG(); if ($GPG) { - $sName = $this->GetActionParam('Name', ''); - $sEmail = $this->GetActionParam('Email', ''); + $sName = $this->GetActionParam('name', ''); + $sEmail = $this->GetActionParam('email', ''); $fingerprint = $GPG->generateKey( $sName ? "{$sName} <{$sEmail}>" : $sEmail, - $this->GetActionParam('Passphrase', '') + $this->GetActionParam('passphrase', '') ); } return $this->DefaultResponse($fingerprint); @@ -156,9 +156,9 @@ trait Pgp public function DoGnupgImportKey() : array { - $sKey = $this->GetActionParam('Key', ''); + $sKey = $this->GetActionParam('key', ''); $sKeyId = $this->GetActionParam('keyId', ''); - $sEmail = $this->GetActionParam('Email', ''); + $sEmail = $this->GetActionParam('email', ''); if (!$sKey) { try { @@ -265,7 +265,7 @@ trait Pgp */ public function DoStorePGPKey() : array { - $key = $this->GetActionParam('Key', ''); + $key = $this->GetActionParam('key', ''); $keyId = $this->GetActionParam('keyId', ''); return $this->DefaultResponse(($key && $keyId && $this->StorePGPKey($key, $keyId))); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php index 130c13a61..a80b8236f 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php @@ -23,7 +23,7 @@ trait Response } return \array_merge(array( -// 'Version' => APP_VERSION, +// 'version' => APP_VERSION, 'Action' => $sActionName, 'Result' => $this->responseObject($mResult) ), $aAdditionalParams); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php index 4a6cec84b..62120eaba 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Themes.php @@ -123,7 +123,7 @@ trait Themes // : \str_replace(';}', '}', \preg_replace('/\\s*([:;{},])\\s*/', '\1', \preg_replace('/\\s+/', ' ', \preg_replace('#/\\*.*?\\*/#s', '', $mResult)))); } - public function UploadBackground(): array + public function UploadBackground(?array $aFile, int $iError): array { $oAccount = $this->getAccountFromToken(); @@ -134,9 +134,6 @@ trait Themes $sName = ''; $sHash = ''; - $aFile = $this->GetActionParam('File', null); - $iError = $this->GetActionParam('Error', \RainLoop\Enumerations\UploadError::UNKNOWN); - if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile)) { $sMimeType = \SnappyMail\File\MimeType::fromFile($aFile['tmp_name'], $aFile['name']) ?: \SnappyMail\File\MimeType::fromFilename($aFile['name']) 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 26b73f83e..3888db991 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ActionsAdmin.php @@ -175,7 +175,7 @@ class ActionsAdmin extends Actions $sPassword = $this->GetActionParam('Password', ''); $this->Logger()->AddSecret($sPassword); - $sNewPassword = $this->GetActionParam('NewPassword', ''); + $sNewPassword = $this->GetActionParam('newPassword', ''); if (\strlen($sNewPassword)) { $this->Logger()->AddSecret($sNewPassword); } @@ -295,11 +295,11 @@ class ActionsAdmin extends Actions } return $this->DefaultResponse(array( - 'Updatable' => \SnappyMail\Repository::canUpdateCore(), - 'Warning' => $bShowWarning, - 'Version' => $sVersion, - 'VersionCompare' => \version_compare(APP_VERSION, $sVersion), - 'Warnings' => $aWarnings + 'updatable' => \SnappyMail\Repository::canUpdateCore(), + 'warning' => $bShowWarning, + 'version' => $sVersion, + 'versionCompare' => \version_compare(APP_VERSION, $sVersion), + 'warnings' => $aWarnings )); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php index 368f0f99e..054bc50bf 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Property.php @@ -190,12 +190,12 @@ class Property implements \JsonSerializable '@Object' => 'Object/PluginProperty', 'value' => $this->mValue, 'placeholder' => $this->sPlaceholder, - 'Name' => $this->sName, - 'Type' => $this->iType, - 'Label' => $this->sLabel, - 'Default' => $this->mDefaultValue, - 'Options' => $this->aOptions, - 'Desc' => $this->sDesc + 'name' => $this->sName, + 'type' => $this->iType, + 'label' => $this->sLabel, + 'default' => $this->mDefaultValue, + 'options' => $this->aOptions, + 'desc' => $this->sDesc ); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/PropertyCollection.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/PropertyCollection.php index 1c59b3bf0..f8f2cd26f 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/PropertyCollection.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/PropertyCollection.php @@ -20,8 +20,8 @@ class PropertyCollection extends \ArrayObject implements \JsonSerializable { return array( '@Object' => 'Object/PluginProperty', - 'Type' => \RainLoop\Enumerations\PluginPropertyType::GROUP, - 'Label' => $this->sLabel, + 'type' => \RainLoop\Enumerations\PluginPropertyType::GROUP, + 'label' => $this->sLabel, 'config' => $this->getArrayCopy() /* 'config' => [ 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 0f7b8abe8..751dcadaf 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,12 +57,12 @@ class Domain extends AbstractProvider public function LoadOrCreateNewFromAction(\RainLoop\Actions $oActions, string $sNameForTest = null) : ?\RainLoop\Model\Domain { - $sName = \mb_strtolower((string) $oActions->GetActionParam('Name', '')); + $sName = \mb_strtolower((string) $oActions->GetActionParam('name', '')); if (\strlen($sName) && $sNameForTest && !\str_contains($sName, '*')) { $sNameForTest = null; } if (\strlen($sName) || $sNameForTest) { - if (!$sNameForTest && !empty($oActions->GetActionParam('Create', 0)) && $this->Load($sName)) { + if (!$sNameForTest && !empty($oActions->GetActionParam('create', 0)) && $this->Load($sName)) { throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::DomainAlreadyExists); } return \RainLoop\Model\Domain::fromArray($sNameForTest ?: $sName, [ diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 4b162948c..1927a8ce3 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -259,14 +259,7 @@ class ServiceActions } if (\method_exists($this->oActions, $sAction) && \is_callable(array($this->oActions, $sAction))) { - $aActionParams = isset($_GET) && \is_array($_GET) ? $_GET : null; - - $aActionParams['File'] = $aFile; - $aActionParams['Error'] = $iError; - - $this->oActions->SetActionParams($aActionParams, $sAction); - - $aResponse = $this->oActions->{$sAction}(); + $aResponse = $this->oActions->{$sAction}($aFile, $iError); } if (!is_array($aResponse)) { diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html index 63b96d0a5..130d710dc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html @@ -1,36 +1,36 @@ - - + + - + - + - + - - + + - +
            - + - - + + - - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsPlugin.html b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsPlugin.html index aedfcc8b0..9004276c6 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsPlugin.html +++ b/snappymail/v/0.0.0/app/templates/Views/Admin/PopupsPlugin.html @@ -14,15 +14,15 @@
            - +
            - +
            - +
            diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsAccount.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsAccount.html index caeb2294a..6339df4fa 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsAccount.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsAccount.html @@ -12,18 +12,18 @@
            -
            -
            - +
          • - +
          • - +