diff --git a/.docker/release/Dockerfile b/.docker/release/Dockerfile index d6675462e..0e6473191 100644 --- a/.docker/release/Dockerfile +++ b/.docker/release/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 -FROM alpine:3.18.5 AS builder -RUN apk add --no-cache php82 php82-json php-phar php-zip +FROM alpine:3.24.3 AS builder +RUN apk add --no-cache php85 php85-json php-phar php-zip RUN apk add --no-cache npm RUN npm install -g gulp yarn WORKDIR /source @@ -23,7 +23,7 @@ RUN set -eux; \ rm -v /snappymail/README.md /snappymail/_include.php # Inspired by the original Rainloop dockerfile from youtous on GitLab -FROM php:8.2-fpm-alpine AS final +FROM php:8.5-fpm-alpine AS final LABEL org.label-schema.description="SnappyMail webmail client image using nginx, php-fpm on Alpine" @@ -99,9 +99,6 @@ RUN set -eux; \ # mysql RUN docker-php-ext-install pdo_mysql -# opcache -RUN docker-php-ext-install opcache - # postgres RUN set -eux; \ apk add --no-cache postgresql-libs; \ @@ -122,6 +119,13 @@ RUN set -eux; \ apk del .build-dependencies; \ apk del .deps +# sqlite +RUN set -eux; \ + apk add --no-cache sqlite-libs; \ + apk add --no-cache --virtual .deps sqlite-dev; \ + docker-php-ext-install pdo_sqlite; \ + apk del .deps + # tidy RUN set -eux; \ apk add --no-cache tidyhtml; \ diff --git a/.docker/release/files/entrypoint.sh b/.docker/release/files/entrypoint.sh index 75e82d729..c0ed7df0c 100755 --- a/.docker/release/files/entrypoint.sh +++ b/.docker/release/files/entrypoint.sh @@ -9,6 +9,12 @@ UPLOAD_MAX_SIZE=${UPLOAD_MAX_SIZE:-25M} MEMORY_LIMIT=${MEMORY_LIMIT:-128M} SECURE_COOKIES=${SECURE_COOKIES:-true} +# Disable listening on ipv6 for ipv4-only environments, or nginx will fail to start +if ! ip a | grep 'inet6 ' > /dev/null; then + echo "[INFO] ipv6 is disabled. Configuring nginx to not listen on ipv6" + sed -i '/listen \[::\]:/d' /etc/nginx/nginx.conf +fi + # Set attachment size limit sed -i "s//$UPLOAD_MAX_SIZE/g" /usr/local/etc/php-fpm.d/php-fpm.conf /etc/nginx/nginx.conf sed -i "s//$MEMORY_LIMIT/g" /usr/local/etc/php-fpm.d/php-fpm.conf diff --git a/.eslintrc.js b/.eslintrc.js index e22cb986d..6b7f26dce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -13,35 +13,35 @@ module.exports = { }, globals: { // SnappyMail - 'rainloopI18N': "readonly", - 'rainloopTEMPLATES': "readonly", - 'rl': "readonly", - 'shortcuts': "readonly", -// '__APP_BOOT': "readonly", + rainloopI18N: 'readonly', + rainloopTEMPLATES: 'readonly', + rl: 'readonly', + shortcuts: 'readonly', +// __APP_BOOT: 'readonly', // deb/boot.js - 'progressJs': "readonly", + progressJs: 'readonly', // others - 'openpgp': "readonly", - 'CKEDITOR': "readonly", - 'Squire': "readonly", - 'SquireUI': "readonly", + openpgp: 'readonly', + CKEDITOR: 'readonly', + Squire: 'readonly', + SquireUI: 'readonly', // node_modules/knockout but dev/External/ko.js is used - 'ko': "readonly", + ko: 'readonly', // vendors/routes/ - 'hasher': "readonly", - 'Crossroads': "readonly", + hasher: 'readonly', + Crossroads: 'readonly', // vendors/jua - 'Jua': "readonly", + Jua: 'readonly', // vendors/bootstrap/bootstrap.native.js - 'BSN': "readonly", + BSN: 'readonly', // Mailvelope - 'mailvelope': "readonly", + mailvelope: 'readonly', // Punycode - 'IDN': "readonly", + IDN: 'readonly', // Turndown - 'TurndownService': "readonly", + TurndownService: 'readonly', // Marked - 'marked': "readonly" + marked: 'readonly' }, // http://eslint.org/docs/rules/ rules: { diff --git a/SECURITY.md b/SECURITY.md index faaba8d68..11d1ddada 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -36,5 +36,5 @@ YMXQVq4SJt7UpWABAORudfJxsBqCRKtPlZMgGTJLjcOkyFJ9C2Fx7DeN0J4I ## Publishing and Credits I will analyze and fix the reported issue as fast as possible. -Together with the reporter I plan the disclosure of the found and fixed vulnerability. +Together with the reporter, I plan the disclosure of the found and fixed vulnerability. Credits to the reporter are granted and can be included in all public communication if desired. diff --git a/build/cpanel.php b/build/cpanel.php index d8280d9e4..ddfab6a6e 100755 --- a/build/cpanel.php +++ b/build/cpanel.php @@ -1,6 +1,9 @@ version}/", file_get_contents($file))); + $cpanel_destination = "{$destPath}snappymail-{$package->version}-cpanel.tar"; @unlink($cpanel_destination); diff --git a/build/plugins.php b/build/plugins.php index 9cb4d9c1e..4561dae03 100755 --- a/build/plugins.php +++ b/build/plugins.php @@ -31,30 +31,48 @@ $keys = [ /* $released = [ 'add-x-originating-ip-header', + 'attachments-force-open', 'avatars', 'backup', 'black-list', + 'cache-apcu', + 'cache-memcache', + 'cache-redis', 'change-password', 'change-password-froxlor', 'change-password-hestia', 'change-password-hmailserver', 'change-password-ispconfig', + 'change-password-mailcow', 'change-password-poppassd', + 'compact-composer', 'custom-login-mapping', + 'haveibeenpwned', + 'ics-viewer', 'imap-contacts-suggestions', 'kolab', 'ldap-contacts-suggestions', 'ldap-identities', 'ldap-login-mapping', 'ldap-mail-accounts', + 'login-autoconfig', + 'login-cpanel', 'login-external', 'login-external-sso', + 'login-gmail', + 'login-http', + 'login-o365', 'login-override', 'login-register', 'login-remote', + 'login-virtuser', 'mailbox-detect', 'nextcloud', 'override-smtp-credentials', + 'proxy-auth', + 'search-filters', + 'security-mxss', + 'send-save-in', 'set-remote-addr', 'smtp-use-from-adr-account', 'snowfall-on-login-screen', diff --git a/cli/compile.php b/cli/compile.php new file mode 100755 index 000000000..936643cc3 --- /dev/null +++ b/cli/compile.php @@ -0,0 +1,247 @@ +#!/usr/bin/php +{'use strict';var aa="function"==typeof Object.defineProperties?Object.defineProperty:function(C,F,E){if(C==Array.prototype||C==Object.prototype)return C;C[F]=E.value;return C};function ba(C){C=["object"==typeof globalThis&&globalThis,C,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var F=0;FE&&(E=Math.max(E+S,0));E'); + // ECMASCRIPT_202 + $data = substr($data, 0, $p1) . substr($data, $p1 + 6, -6) . "\n"; + // ECMASCRIPT_2018 +// $data = substr($data, 0, $p1) . substr($data, $p1 + 19, -6) . "\n"; + // ECMASCRIPT6 +// $data = substr($data, 0, $p1) . substr($data, strpos($data, 'return!0}return!1}});') + 22, -6) . "\n"; + + file_put_contents($file, $data); + exit; +} + +if (isset($options['openpgp'])) { + chdir(ROOT_DIR . '/vendors/openpgp-5'); + execCmd('git pull', $return_var); + execCmd('./build.sh', $return_var); + exit; +} + +if (isset($options['squire'])) { + chdir(ROOT_DIR . '/vendors/squire2'); +// execCmd('git pull', $return_var); +// execCmd('git checkout snappymail', $return_var); + execCmd('./build.sh', $return_var); + exit; +} + +if (isset($options['source'])) { + #cd ROOT_DIR . '/vendors/openpgp-2.6.2/'; + #rollup -c + #npm cache clean --force + + if ($gulp = trim(`which gulp`)) { + execCmd($gulp, $return_var); + } else { + exit('gulp not installed, run as root: npm install --global gulp-cli'); + } + + #rollup -c + + $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"; +// execCmd("{$gzip} -k --best {$cmddir}/js/*.js"); + execCmd("{$gzip} -k --best {$cmddir}/js/min/*.js"); + execCmd("{$gzip} -k --best {$cmddir}/css/admin*.css"); + execCmd("{$gzip} -k --best {$cmddir}/css/app*.css"); +// unlink($cmddir . '/js/boot.js.gz'); +// unlink($cmddir . '/js/min/boot.min.js.gz'); + } + + if ($brotli = trim(`which brotli`)) { + echo "\x1b[33;1m === Brotli *.js and *.css === \x1b[0m\n"; +// execCmd("{$brotli} -k --best {$cmddir}/js/*.js"); + execCmd("{$brotli} -k --best {$cmddir}/js/min/*.js"); + execCmd("{$brotli} -k --best {$cmddir}/css/admin*.css"); + execCmd("{$brotli} -k --best {$cmddir}/css/app*.css"); +// unlink($cmddir . '/js/boot.js.br'); +// unlink($cmddir . '/js/min/boot.min.js.br'); + } +} + +if (isset($options['readme'])) { + + $basedir = escapeshellcmd(ROOT_DIR) . '/snappymail/v/0.0.0'; + + $readme = file_get_contents(ROOT_DIR . '/README.md'); + + $total = filesize("{$basedir}/static/js/serviceworker.js"); + foreach (['admin','app','boot','libs','sieve'] as $name) { + $size = filesize("{$basedir}/static/js/{$name}.js"); + $total += $size; + $readme = preg_replace("/(\\|{$name}\\.js\\s+\\|[\\s0-9.]+\\|)[\\s0-9.]+\\|/Ds", + '$1' . str_pad(number_format($size, 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' |', + $readme + ); + } + $readme = preg_replace("/(\\|TOTAL\\s+\\|8[\\s0-9.]+\\|)[\\s0-9.]+\\|/Ds", + '$1' . str_pad(number_format($total, 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' |', + $readme + ); + + $total = []; + $total_br = []; + $total_gz = []; + foreach (['admin','app','boot','libs','sieve'] as $name) { + $size = filesize("{$basedir}/static/js/min/{$name}.min.js"); + $size_br = filesize("{$basedir}/static/js/min/{$name}.min.js.br"); + $size_gz = filesize("{$basedir}/static/js/min/{$name}.min.js.gz"); + $total[$name] = $size; + $total_br[$name] = $size_br; + $total_gz[$name] = $size_gz; + $readme = preg_replace( + "/(\\|{$name}\\.min\\.js\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format($size, 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format($size_gz, 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$3|' + . str_pad(number_format($size_br, 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + } + $readme = preg_replace( + "/(TOTAL user\+sieve\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format(array_sum($total) - $total['admin'], 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format(array_sum($total_gz) - $total_gz['admin'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$3|' + . str_pad(number_format(array_sum($total_br) - $total_br['admin'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + + //$smaller = 1217635 / (array_sum($total) - $total['admin']); + $smaller = 358761 / (array_sum($total_gz) - $total_gz['admin']); + //$smaller = 299485 / (array_sum($total_br) - $total_br['admin']); + $readme = preg_replace( + '/(For a user it is around) [0-9]+/', + '$1 ' . round(100 - 100 / $smaller), + $readme + ); + + unset($total['sieve']); + unset($total_br['sieve']); + unset($total_gz['sieve']); + $readme = preg_replace( + "/(TOTAL user\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format(array_sum($total) - $total['admin'], 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format(array_sum($total_gz) - $total_gz['admin'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$3|' + . str_pad(number_format(array_sum($total_br) - $total_br['admin'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + $readme = preg_replace( + "/(TOTAL admin\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format(array_sum($total) - $total['app'], 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format(array_sum($total_gz) - $total_gz['app'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$3|' + . str_pad(number_format(array_sum($total_br) - $total_br['app'], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + + foreach (['admin','app'] as $name) { + $readme = preg_replace( + "/(\\|{$name}\\.css\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.css"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.css.gz"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' |' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.css.br"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + $readme = preg_replace( + "/(\\|{$name}\\.min\\.css\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.min.css"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$2|' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.min.css.gz"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' |' + . str_pad(number_format(filesize("{$basedir}/static/css/{$name}.min.css.br"), 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + } + + + $sizes = [ + filesize("{$basedir}/static/js/min/openpgp.min.js"), + filesize("{$basedir}/static/js/min/openpgp.min.js.gz"), + filesize("{$basedir}/static/js/min/openpgp.min.js.br") + ]; + //|openpgp.min.js | 330.742 | 540.542 |102.388 | 167.397 | 84.241 | 137.502 | + $readme = preg_replace( + "/(openpgp\.min\.js\\s+\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+(\\|[\\s0-9.]+)\\|[\\s0-9.]+\\|/Ds", + '$1|' + . str_pad(number_format($sizes[0], 0, '', '.'), 9, ' ', STR_PAD_LEFT) . ' ' + . '$2| ' + . str_pad(number_format($sizes[1], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '$3| ' + . str_pad(number_format($sizes[2], 0, '', '.'), 7, ' ', STR_PAD_LEFT) . ' ' + . '|' + , + $readme + ); + + //file:// + + file_put_contents(ROOT_DIR . '/README.md', $readme); + + $data = file_put_contents( + "{$basedir}/app/templates/Views/Admin/AdminSettingsAbout.html", + preg_replace( + '/20[0-9][0-9] ©/', + date('Y').' ©', + file_get_contents("{$basedir}/app/templates/Views/Admin/AdminSettingsAbout.html") + ) + ); +} diff --git a/cli/release.php b/cli/release.php index aac915b02..fd17657b0 100755 --- a/cli/release.php +++ b/cli/release.php @@ -2,6 +2,7 @@ open($zip_destination, ZIPARCHIVE::CREATE)) { exit("Failed to create {$zip_destination}"); } -$tar = new PharData($tar_destination); +$tar_destination .= '.gz'; +$tar = new \SnappyMail\TAR($tar_destination); +//$tar = new PharData($tar_destination); $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST); foreach ($files as $file) { @@ -158,10 +161,13 @@ $zip->addFile('README.md'); $tar->addFile('README.md'); $zip->close(); - -$tar->compress(Phar::GZ); -unlink($tar_destination); -$tar_destination .= '.gz'; +if ($tar instanceof PharData) { + $tar->compress(Phar::GZ); + unlink($tar_destination); + $tar_destination .= '.gz'; +} else { + $tar->close(); +} echo "{$zip_destination} created\n{$tar_destination} created\n"; @@ -188,7 +194,7 @@ file_put_contents("{$destPath}core.json", '{ // Arch User Repository if ($options['aur']) { // extension_loaded('blake2') - if (!function_exists('b2sum') && $b2sum = trim(`which b2sum`)) { + if (!function_exists('b2sum') && $b2sum = trim(shell_exec('which b2sum'))) { function b2sum($file) { $file = escapeshellarg($file); exec("b2sum --binary {$file} 2>&1", $output, $exitcode); @@ -244,7 +250,7 @@ else if ($options['docker']) { $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}"); + passthru("{$docker} build --pull -f " . ROOT_DIR . '/.docker/release/Dockerfile ' . ROOT_DIR . " --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}"); } else { echo "Docker not installed!\n"; } @@ -252,28 +258,27 @@ else if ($options['docker']) { 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); + $gpg = 'gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB '; + $gpg_sign = $gpg . '--armor --detach-sign '; + passthru($gpg_sign . escapeshellarg($tar_destination), $return_var); + passthru($gpg_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); + passthru($gpg_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); + passthru($gpg_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); + passthru($gpg_sign . escapeshellarg("{$destPath}snappymail-{$package->version}-cpanel.tar.gz"), $return_var); } if (isset($options['debian'])) { - passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign ' + passthru($gpg_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 ' + passthru($gpg . '--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 ' + passthru($gpg . '--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/dev/App/User.js b/dev/App/User.js index aafec7da3..5e28164ac 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -1,6 +1,7 @@ import 'External/User/ko'; import { SMAudio } from 'Common/Audio'; +import { arrayLength } from 'Common/Utils'; import { mailToHelper, setLayoutResizer, dropdownsDetectVisibility, loadAccountsAndIdentities } from 'Common/UtilsUser'; import { @@ -240,7 +241,15 @@ export class AppUser extends AbstractApp { showMessageComposer(params = []) { - showScreenPopup(ComposePopupView, params); + let msg = params[1]; + if (1 == arrayLength(msg)) { + msg = msg[0]; + } + if (msg) { + msg.decrypt().then((/*success*/)=>showScreenPopup(ComposePopupView, params)); + } else { + showScreenPopup(ComposePopupView, params); + } } } @@ -251,7 +260,8 @@ AskPopupView.password = function(sAskDesc, btnText, ask) { view => resolve({ password:view.passphrase(), username:/*ask & 2 ? */view.username(), - remember:/*ask & 4 ? */view.remember() + remember:/*ask & 4 ? */view.remember(), + rememberPermanent: view.rememberPermanent(), }), () => resolve(null), true, diff --git a/dev/Common/Html.js b/dev/Common/Html.js index 0be300ec9..af068489b 100644 --- a/dev/Common/Html.js +++ b/dev/Common/Html.js @@ -232,6 +232,7 @@ export const detectHiddenImages = true, // !!SettingsGet('try_to_detect_hidden_images'), bqLevel = parseInt(SettingsUserStore.maxBlockquotesLevel()), + minFontSize = parseInt(SettingsUserStore.minFontSize()), result = { hasExternals: false, @@ -257,6 +258,15 @@ export const rightmargin: (value, node) => node.style.marginRight = pInt(value) + 'px' }, allowedAttributes = [ + // SnappyMail +// 'data-x-href-broken', +// 'data-x-href-tracking', + 'data-x-src', +// 'data-x-src-hidden', +// 'data-x-src-tracking', +// 'data-x-src-broken', +// 'data-x-style-url', +// 'data-x-style-broken-urls', // defaults 'name', 'dir', 'lang', 'style', 'title', @@ -299,8 +309,13 @@ export const // Not supported by