On Mon, 25 Sep 2023 at 15:15:57 +0200, Guilhem Moulin wrote: > [ Other info ] > > In addition to the debdiff.gz between 1.6.1+dfsg-1 (bookworm) and 1.6.3+dfsg-1~deb12u1, > I attach a patch-applied diff excluding upstream's tests/**, program/localization/**, > and plugins/*/localization/**, which should more accurately show what > this p-u is about. Now with the attachement :-) > If you think that 1.6.3+dfsg-1~deb12u1 is beyond the scope of bookworm-pu then > I'll prepare another upload, this time backporting the aforementioned > regressions and security issue instead of following the upstream stable > branch. -- Guilhem.
diff --git a/.github/run.sh b/.github/run.sh
index 6a16b2b53..e15fbe47e 100755
--- a/.github/run.sh
+++ b/.github/run.sh
@@ -3,8 +3,8 @@
# The script is intended for use on Travis with Trusty distribution
# It installs in-browser tests dependencies and prepares Roundcube instance
-GMV=1.5.11
-CHROMEVERSION=$(google-chrome-stable --version | tr -cd [:digit:]. | cut -d . -f 1)
+GMV=1.6.13
+CHROMEVERSION=$(google-chrome-stable --version | tr -cd [:digit:].)
GMARGS="-Dgreenmail.setup.all -Dgreenmail.users=test:test -Dgreenmail.startup.timeout=3000"
# Make temp and logs writeable
diff --git a/.github/workflows/browser_tests.yml b/.github/workflows/browser_tests.yml
index 8c1308f7f..5798ad8f5 100644
--- a/.github/workflows/browser_tests.yml
+++ b/.github/workflows/browser_tests.yml
@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -41,10 +41,7 @@ jobs:
- name: Setup composer
run: |
cp composer.json-dist composer.json
- composer require "laravel/dusk:~6.9.0" --no-update
-
- - name: Fix PHPUnit for PHP8
- run: composer config platform.php 7.4
+ composer require "laravel/dusk:^7.9" --no-update
- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index a9b13e330..af41d3b21 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -12,13 +12,13 @@ jobs:
strategy:
fail-fast: true
matrix:
- php: ["7.3", "7.4", "8.0", "8.1"]
+ php: ["7.3", "7.4", "8.0", "8.1", "8.2", "8.3"]
name: PHP ${{ matrix.php }}/Linux
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -39,9 +39,9 @@ jobs:
cp composer.json-dist composer.json
composer require "kolab/net_ldap3:~1.1.1" --no-update
- - name: Fix PHPUnit for PHP 8.1
- run: composer config platform.php 8.0
- if: matrix.php >= 8.1
+ - name: Fix PHPUnit for PHP 8.2
+ run: composer config platform.php 8.1
+ if: matrix.php >= 8.2
- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-progress
diff --git a/.tx/config b/.tx/config
index b6e5674a2..e86eabe63 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,99 +1,99 @@
[main]
-host = https://www.transifex.com
-lang_map = be: be_BE, sr: sr_CS, fa: fa_AF, lb: lb_LU, fr: fr_FR
-type = PHP_ALT_ARRAY
+host = https://www.transifex.com
+lang_map = sr: sr_CS, fa: fa_AF, lb: lb_LU, fr: fr_FR, be: be_BE
-[roundcube-webmail.labels]
+[o:roundcube:p:roundcube-webmail:r:labels]
file_filter = program/localization/<lang>/labels.inc
source_file = program/localization/en_US/labels.inc
source_lang = en_US
-[roundcube-webmail.messages]
+[o:roundcube:p:roundcube-webmail:r:messages]
file_filter = program/localization/<lang>/messages.inc
source_file = program/localization/en_US/messages.inc
source_lang = en_US
-[roundcube-webmail.timezones]
-file_filter = program/localization/<lang>/timezones.inc
-source_file = program/localization/en_US/timezones.inc
-source_lang = en_US
-
-[roundcube-webmail.plugin-acl]
+[o:roundcube:p:roundcube-webmail:r:plugin-acl]
file_filter = plugins/acl/localization/<lang>.inc
source_file = plugins/acl/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-archive]
+[o:roundcube:p:roundcube-webmail:r:plugin-archive]
file_filter = plugins/archive/localization/<lang>.inc
source_file = plugins/archive/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-attachment_reminder]
+[o:roundcube:p:roundcube-webmail:r:plugin-attachment_reminder]
file_filter = plugins/attachment_reminder/localization/<lang>.inc
source_file = plugins/attachment_reminder/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-emoticons]
+[o:roundcube:p:roundcube-webmail:r:plugin-emoticons]
file_filter = plugins/emoticons/localization/<lang>.inc
source_file = plugins/emoticons/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-enigma]
+[o:roundcube:p:roundcube-webmail:r:plugin-enigma]
file_filter = plugins/enigma/localization/<lang>.inc
source_file = plugins/enigma/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-help]
+[o:roundcube:p:roundcube-webmail:r:plugin-help]
file_filter = plugins/help/localization/<lang>.inc
source_file = plugins/help/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-hide_blockquote]
+[o:roundcube:p:roundcube-webmail:r:plugin-hide_blockquote]
file_filter = plugins/hide_blockquote/localization/<lang>.inc
source_file = plugins/hide_blockquote/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-managesieve]
+[o:roundcube:p:roundcube-webmail:r:plugin-managesieve]
file_filter = plugins/managesieve/localization/<lang>.inc
source_file = plugins/managesieve/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-markasjunk]
+[o:roundcube:p:roundcube-webmail:r:plugin-markasjunk]
file_filter = plugins/markasjunk/localization/<lang>.inc
source_file = plugins/markasjunk/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-new_user_dialog]
+[o:roundcube:p:roundcube-webmail:r:plugin-new_user_dialog]
file_filter = plugins/new_user_dialog/localization/<lang>.inc
source_file = plugins/new_user_dialog/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-newmail_notifier]
+[o:roundcube:p:roundcube-webmail:r:plugin-newmail_notifier]
file_filter = plugins/newmail_notifier/localization/<lang>.inc
source_file = plugins/newmail_notifier/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-password]
+[o:roundcube:p:roundcube-webmail:r:plugin-password]
file_filter = plugins/password/localization/<lang>.inc
source_file = plugins/password/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-subscriptions_option]
+[o:roundcube:p:roundcube-webmail:r:plugin-subscriptions_option]
file_filter = plugins/subscriptions_option/localization/<lang>.inc
source_file = plugins/subscriptions_option/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-userinfo]
+[o:roundcube:p:roundcube-webmail:r:plugin-userinfo]
file_filter = plugins/userinfo/localization/<lang>.inc
source_file = plugins/userinfo/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-vcard_attachments]
+[o:roundcube:p:roundcube-webmail:r:plugin-vcard_attachments]
file_filter = plugins/vcard_attachments/localization/<lang>.inc
source_file = plugins/vcard_attachments/localization/en_US.inc
source_lang = en_US
-[roundcube-webmail.plugin-zipdownload]
+[o:roundcube:p:roundcube-webmail:r:plugin-zipdownload]
file_filter = plugins/zipdownload/localization/<lang>.inc
source_file = plugins/zipdownload/localization/en_US.inc
source_lang = en_US
+
+[o:roundcube:p:roundcube-webmail:r:timezones]
+file_filter = program/localization/<lang>/timezones.inc
+source_file = program/localization/en_US/timezones.inc
+source_lang = en_US
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b8635951..f6dc05b10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,54 @@
# Changelog Roundcube Webmail
+## Unreleased
+
+- Fix bug where installto.sh/update.sh scripts were removing some essential options from the config file (#9051)
+- Update jQuery-UI to version 1.13.2 (#9041)
+- Fix regression that broke use_secure_urls feature (#9052)
+- Fix potential PHP fatal error when opening a message with message/rfc822 part (#8953)
+- Fix bug where a duplicate `<title>` tag in HTML email could cause some parts being cut off (#9029)
+- Fix bug where a list of folders could have been sorted incorrectly (#9057)
+- Fix regression where LDAP addressbook 'filter' option was ignored (#9061)
+- Fix wrong order of a multi-folder search result when sorting by size (#9065)
+- Fix so install/update scripts do not require PEAR (#9037)
+- Fix regression where some mail parts could have been decoded incorrectly, or not at all (#9096)
+- Fix handling of an error case in Cyrus IMAP BINARY FETCH, fallback to non-binary FETCH (#9097)
+- Fix PHP8 deprecation warning in the reconnect plugin (#9083)
+- Fix "Show source" on mobile with x_frame_options = deny (#9084)
+- Fix various PHP warnings (#9098)
+- Fix deprecated use of ldap_connect() in password's ldap_simple driver (#9060)
+- Fix cross-site scripting (XSS) vulnerability in handling of linkrefs in plain text messages
+
+## Release 1.6.2
+
+- Add Uyghur localization
+- Fix regression in OAuth request URI caused by use of REQUEST_URI instead of SCRIPT_NAME as a default (#8878)
+- Fix bug where false attachment reminder was displayed on HTML mail with inline images (#8885)
+- Fix bug where a non-ASCII character in app.js could cause error in javascript engine (#8894)
+- Fix JWT decoding with url safe base64 schema (#8890)
+- Fix bug where .wav instead of .mp3 file was used for the new mail notification in Firefox (#8895)
+- Fix PHP8 warning (#8891)
+- Fix support for Windows-31J charset (#8869)
+- Fix so LDAP VLV option is disabled by default as documented (#8833)
+- Fix so an email address with name is supported as input to the managesieve notify :from parameter (#8918)
+- Fix Help plugin menu (#8898)
+- Fix invalid onclick handler on the logo image when using non-array skin_logo setting (#8933)
+- Fix duplicate recipients in "To" and "Cc" on reply (#8912)
+- Fix bug where it wasn't possible to scroll lists by clicking middle mouse button (#8942)
+- Fix bug where label text in a single-input dialog could be partially invisible in some locales (#8905)
+- Fix bug where LDAP (fulltext) search didn't work without 'search_fields' in config (#8874)
+- Fix extra leading newlines in plain text converted from HTML (#8973)
+- Fix so recipients with a domain ending with .s are allowed (#8854)
+- Fix so vCard output does not contain non-standard/redundant TYPE=OTHER and TYPE=INTERNET (#8838)
+- Fix QR code images for contacts with non-ASCII characters (#9001)
+- Fix PHP8 warnings when using list_flags and list_cols properties by plugins (#8998)
+- Fix bug where subfolders could loose subscription on parent folder rename (#8892)
+- Fix connecting to LDAP using an URI with ldapi:// scheme (#8990)
+- Fix insecure shell command params handling in cmd_learn driver of markasjunk plugin (#9005)
+- Fix bug where some mail headers didn't work in cmd_learn driver of markasjunk plugin (#9005)
+- Fix PHP fatal error when importing vcf file using PHP 8.2 (#9025)
+- Fix so output of log_date_format with microseconds contains time in server time zone, not UTC
+
## Release 1.6.1
- Kill session if refreshing oauth token fails (#8734)
diff --git a/Makefile b/Makefile
index 0c0288123..36a8ac610 100644
--- a/Makefile
+++ b/Makefile
@@ -3,11 +3,12 @@
#
GITREMOTE=https://github.com/roundcube/roundcubemail.git
-GITBRANCH=master
+GITBRANCH=release-1.6
GPGKEY=devs@roundcube.net
VERSION=1.6-git
SEDI=sed -i
WHICH=which
+PHP_VERSION=7.3
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
@@ -19,8 +20,13 @@ all: clean complete dependent framework
complete: roundcubemail-git
cp -RH roundcubemail-git roundcubemail-$(VERSION)
- (cd roundcubemail-$(VERSION); jq '.require += {"kolab/net_ldap3": "~1.1.1"} | del(.suggest."kolab/net_ldap3")' --indent 4 composer.json-dist > composer.json)
- (cd roundcubemail-$(VERSION); php /tmp/composer.phar install --prefer-dist --no-dev --ignore-platform-reqs --no-interaction)
+ (cd roundcubemail-$(VERSION); cp composer.json-dist composer.json)
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar config platform.php $(PHP_VERSION))
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar require "kolab/net_ldap3:~1.1.1" --no-update --no-install)
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar config --unset suggest.kolab/net_ldap3)
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar config --unset require-dev)
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar install --prefer-dist --no-dev --no-interaction)
+ (cd roundcubemail-$(VERSION); php /tmp/composer.phar config --unset platform)
(cd roundcubemail-$(VERSION); bin/install-jsdeps.sh --force)
(cd roundcubemail-$(VERSION); bin/jsshrink.sh program/js/publickey.js; bin/jsshrink.sh plugins/managesieve/codemirror/lib/codemirror.js)
(cd roundcubemail-$(VERSION); rm -f jsdeps.json bin/install-jsdeps.sh *.orig; rm -rf temp/js_cache)
@@ -73,7 +79,6 @@ buildtools: /tmp/composer.phar
npm install lessc
npm install less-plugin-clean-css
npm install csso-cli
- @$(WHICH) jq || echo "!!!!!! Please install jq (https://stedolan.github.io/jq/) !!!!!!"
/tmp/composer.phar:
curl -sS https://getcomposer.org/installer | php -- --install-dir=/tmp/
diff --git a/SQL/mysql/2016112200.sql b/SQL/mysql/2016112200.sql
index 62c974946..1bfc9ef64 100644
--- a/SQL/mysql/2016112200.sql
+++ b/SQL/mysql/2016112200.sql
@@ -1,4 +1,5 @@
-ALTER TABLE `dictionary` ADD COLUMN `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; -- redundant, for compat. with Galera Cluster
+-- redundant column, for compat. with Galera Cluster
+ALTER TABLE `dictionary` ADD COLUMN `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
DROP TABLE `cache`;
DROP TABLE `cache_shared`;
diff --git a/bin/transifexpull.sh b/bin/transifexpull.sh
index a8f150288..ad78fbc2d 100755
--- a/bin/transifexpull.sh
+++ b/bin/transifexpull.sh
@@ -1,6 +1,9 @@
#!/bin/sh
TX=`which tx`
+PWD=`dirname "$0"`
+
+cd $PWD/..
# In 'translator' mode files will contain empty translated texts
# where translation is not available, we'll remove these later
@@ -8,9 +11,7 @@ TX=`which tx`
# Note: there's a bug in txclib, so if the command below doesn't
# work see https://github.com/transifex/transifex-client/commit/a80320735973dd608b48520bf3b89ad53e2b088b
-$TX --debug pull -a -f --mode translator
-
-PWD=`dirname "$0"`
+$TX pull -a -f --mode translator
do_clean()
{
@@ -32,15 +33,15 @@ do_clean()
}
# clean up translation files
-for file in $PWD/../program/localization/*/*.inc; do
+for file in program/localization/*/*.inc; do
do_clean $file
done
-for file in $PWD/../plugins/*/localization/*.inc; do
+for file in plugins/*/localization/*.inc; do
do_clean $file
done
# remove empty localization files
-for file in $PWD/../program/localization/*/labels.inc; do grep -q -E '\$labels' $file || rm $file; done
-for file in $PWD/../program/localization/*/timezones.inc; do grep -q -E '\$labels' $file || rm $file; done
-for file in $PWD/../program/localization/*/messages.inc; do grep -q -E '\$messages' $file || rm $file; done
-for file in $PWD/../plugins/*/localization/*.inc; do grep -q -E '\$(labels|messages)' $file || rm $file; done
+for file in program/localization/*/labels.inc; do grep -q -E '\$labels' $file || rm $file; done
+for file in program/localization/*/timezones.inc; do grep -q -E '\$labels' $file || rm $file; done
+for file in program/localization/*/messages.inc; do grep -q -E '\$messages' $file || rm $file; done
+for file in plugins/*/localization/*.inc; do grep -q -E '\$(labels|messages)' $file || rm $file; done
diff --git a/bin/update.sh b/bin/update.sh
index a35dce347..de6c10cc2 100755
--- a/bin/update.sh
+++ b/bin/update.sh
@@ -90,7 +90,7 @@ if ($RCI->configured) {
$error = $written = false;
if (!DEBIAN_PKG) {
- echo ". backing up the current config file(s)...\n";
+ echo "- backing up the current config file(s)...\n";
foreach (['config', 'main', 'db'] as $file) {
if (file_exists(RCMAIL_CONFIG_DIR . '/' . $file . '.inc.php')) {
@@ -103,7 +103,7 @@ if ($RCI->configured) {
if (!$error) {
$RCI->merge_config();
- echo ". writing " . RCMAIL_CONFIG_DIR . "/config.inc.php".(DEBIAN_PKG ? ".dpkg-new" : "")."...\n";
+ echo "- writing " . RCMAIL_CONFIG_DIR . "/config.inc.php".(DEBIAN_PKG ? ".dpkg-new" : "")."...\n";
$written = $RCI->save_configfile($RCI->create_config(false));
}
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
index b615f7d78..4338ec927 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -311,11 +311,11 @@ $config['smtp_timeout'] = 0;
// The example below enables server certificate validation, and
// requires 'smtp_timeout' to be non zero.
// $config['smtp_conn_options'] = [
-// 'ssl' => [
-// 'verify_peer' => true,
-// 'verify_depth' => 3,
-// 'cafile' => '/etc/openssl/certs/ca.crt',
-// ],
+// 'ssl' => [
+// 'verify_peer' => true,
+// 'verify_depth' => 3,
+// 'cafile' => '/etc/openssl/certs/ca.crt',
+// ],
// ];
// Note: These can be also specified as an array of options indexed by hostname
$config['smtp_conn_options'] = null;
@@ -799,6 +799,7 @@ $config['no_save_sent_messages'] = false;
// Warning: This requires http server configuration. Sample:
// RewriteRule ^/roundcubemail/[a-zA-Z0-9]{16}/(.*) /roundcubemail/$1 [PT]
// Alias /roundcubemail /var/www/roundcubemail/
+// Warning: This feature does NOT work with request_path = 'SCRIPT_NAME'
// Note: Use assets_path to not prevent the browser from caching assets
$config['use_secure_urls'] = false;
@@ -808,7 +809,7 @@ $config['use_secure_urls'] = false;
// The reverse proxy config can specify a custom header (e.g. X-Forwarded-Path) containing
// the path under which Roundcube is exposed to the outside world (e.g. /rcube/).
// This header value is then available in PHP with $_SERVER['HTTP_X_FORWARDED_PATH'].
-// By default the path comes from 'REQUEST_URI', 'REDIRECT_SCRIPT_URL' or 'SCRIPT_NAME',
+// By default the path comes from 'REDIRECT_SCRIPT_URL', 'SCRIPT_NAME' or 'REQUEST_URI',
// whichever is set (in this order).
$config['request_path'] = null;
@@ -1098,10 +1099,12 @@ $config['ldap_public']['Verisign'] = [
// to be one of the search_fields, the base of base_dn is appended
// to the RDN to insert into the LDAP directory.
'LDAP_rdn' => 'cn',
- // The required fields needed to build a new contact as required by
+ // The required attributes needed to build a new contact as required by
// the object classes (can include additional fields not required by the object classes).
'required_fields' => ['cn', 'sn', 'mail'],
- 'search_fields' => ['mail', 'cn'], // fields to search in
+ // The attributes used when searching with "All fields" option
+ // If empty, attributes for name, surname, firstname and email fields will be used
+ 'search_fields' => ['mail', 'cn'],
// mapping of contact fields to directory attributes
// 1. for every attribute one can specify the number of values (limit) allowed.
// default is 1, a wildcard * means unlimited
diff --git a/debian/README.source b/debian/README.source
index 215fd8447..fc72e3815 100644
--- a/debian/README.source
+++ b/debian/README.source
@@ -22,9 +22,7 @@ following:
1. Merge the newest upstream release tag into the 'debian/latest'
branch of your packaging repository:
- $ UPSTREAM_VERSION=1.6.0
- $ gbp import-orig --uscan --upstream-version=$UPSTREAM_VERSION+dfsg \
- --upstream-vcs-tag=$UPSTREAM_VERSION
+ $ gbp import-orig --uscan
That commands does all the magic, namely
- updating the `upstream` remote,
@@ -34,12 +32,10 @@ following:
as additional parent, and
- merging 'upstream/$VERSION' into 'debian/latest'
- We use --upstream-version and --upstream-vcs-tag since it doesn't
- trim the +dfsg repack suffix, see https://bugs.debian.org/1009163 .
-
2. Optionally, move upstream tarballs and detached signatures to the
build area:
+ $ UPSTREAM_VERSION=1.6.2
$ mv -vt "$XDG_CACHE_HOME/build-area" ../roundcube[-_]"$UPSTREAM_VERSION"*.tar.*
This step is needed when gbp.conf(5) sets a non-default export-dir, see
diff --git a/debian/changelog b/debian/changelog
index 8bdf27139..9110c1f06 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,46 @@
+roundcube (1.6.3+dfsg-1~deb12u1) bookworm; urgency=medium
+
+ * Rebuild for bookworm.
+ * Salsa CI: Set RELEASE=bookworm.
+ * d/gbp.conf: Set --debian-branch=debian/bookworm.
+
+ -- Guilhem Moulin <guilhem@debian.org> Mon, 25 Sep 2023 14:22:10 +0200
+
+roundcube (1.6.3+dfsg-1) unstable; urgency=medium
+
+ * New upstream security and bugfix release:
+ + Fix CVE-2023-43770: cross-site scripting (XSS) vulnerability in handling
+ of linkrefs in plain text messages. (Closes: #1052059)
+ + Fix regression that broke use_secure_urls feature hence OAuth2
+ authentication. (Closes: #1050317)
+ + Fix regression where LDAP addressbook 'filter' option was ignored.
+ + Fix regression in decoding mail parts FETCHed from IMAP.
+ + Fix PHP8 warnings.
+ * roundcube-core.cron: Trigger gc twice every hour. (Closes: #1043395)
+ * Fix GuzzleHttp autoload location. (Closes: #1040705)
+ * d/p/fix-autoload-location.patch: Set ‘Forwarded: not-needed’ DEP-3 header.
+ * Refresh d/patches.
+
+ -- Guilhem Moulin <guilhem@debian.org> Mon, 18 Sep 2023 14:18:17 +0200
+
+roundcube (1.6.2+dfsg-1) unstable; urgency=medium
+
+ [ Amin Bandali ]
+ * Test suite: Adjust short date test to make it work with all ICUs.
+ (Closes: #1030161)
+
+ [ Remus-Gabriel Chelu ]
+ * Add Romanian debconf templates translation. (Closes: #1033468)
+
+ [ Guilhem Moulin ]
+ * New upstream bugfix release.
+ * d/gbp.conf, d/README.source: Remove obsolete comment.
+ * d/sql/mysql/1.3.0-1: Move inline comment.
+ * d/p/fix-short-date-test-icu72.patch: Remove patch applied upstream.
+ * Refresh patches.
+
+ -- Guilhem Moulin <guilhem@debian.org> Sun, 02 Jul 2023 11:54:33 +0200
+
roundcube (1.6.1+dfsg-1) unstable; urgency=medium
* New upstream bugfix release.
diff --git a/debian/gbp.conf b/debian/gbp.conf
index 771f71bc5..8ed295e5d 100644
--- a/debian/gbp.conf
+++ b/debian/gbp.conf
@@ -1,12 +1,10 @@
[DEFAULT]
-debian-branch = debian/latest
+debian-branch = debian/bookworm
upstream-branch = upstream/release-1.6
pristine-tar = True
components = ["tinymce", "tinymce-langs"]
[import-orig]
-# XXX the +dfsg suffix isn't stripped so for now we have to manually use
-# `v=1.5.1; gbp import-orig --uscan --upstream-version=$v+dfsg --upstream-vcs-tag=$v`
upstream-vcs-tag = %(version)s
[pq]
diff --git a/debian/patches/default-charset-utf8.patch b/debian/patches/default-charset-utf8.patch
index e454e82cd..0894ad216 100644
--- a/debian/patches/default-charset-utf8.patch
+++ b/debian/patches/default-charset-utf8.patch
@@ -8,10 +8,10 @@ Forwarded: not-needed
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
-index 77e843f..b615f7d 100644
+index 00f1a8d..4338ec9 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
-@@ -1256,7 +1256,7 @@ $config['collected_senders'] = true;
+@@ -1259,7 +1259,7 @@ $config['collected_senders'] = true;
// ----------------------------------
// Use this charset as fallback for message decoding
diff --git a/debian/patches/dont-force-set-session.gc_probability=1.patch b/debian/patches/dont-force-set-session.gc_probability=1.patch
index 824ca39a0..b9c90513e 100644
--- a/debian/patches/dont-force-set-session.gc_probability=1.patch
+++ b/debian/patches/dont-force-set-session.gc_probability=1.patch
@@ -16,7 +16,7 @@ This reverts upstream commit 32a0ad6778cde495e30f3447e5220136f0528cee.
1 file changed, 7 deletions(-)
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
-index 31fba2e..1f75526 100644
+index 83ef67a..57e4d0f 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -450,7 +450,6 @@ class rcube
diff --git a/debian/patches/fix-BaconQrCode-autoload-location.patch b/debian/patches/fix-BaconQrCode-autoload-location.patch
deleted file mode 100644
index 9c45fdb6a..000000000
--- a/debian/patches/fix-BaconQrCode-autoload-location.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From: Guilhem Moulin <guilhem@debian.org>
-Date: Sun, 13 Mar 2022 00:08:19 +0100
-Subject: Fix BaconQrCode's autoload location
-
-Snippet generated with `phpabtpl --suggest bacon/bacon-qr-code`.
----
- program/actions/contacts/qrcode.php | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/program/actions/contacts/qrcode.php b/program/actions/contacts/qrcode.php
-index 3511c79..8ab71e8 100644
---- a/program/actions/contacts/qrcode.php
-+++ b/program/actions/contacts/qrcode.php
-@@ -17,6 +17,9 @@
- +-----------------------------------------------------------------------+
- */
-
-+if (stream_resolve_include_path('Bacon/BaconQrCode/autoload.php'))
-+ include_once('Bacon/BaconQrCode/autoload.php');
-+
- class rcmail_action_contacts_qrcode extends rcmail_action_contacts_index
- {
- protected static $mode = self::MODE_HTTP;
diff --git a/debian/patches/fix-autoload-locations.patch b/debian/patches/fix-autoload-locations.patch
new file mode 100644
index 000000000..adb4b30de
--- /dev/null
+++ b/debian/patches/fix-autoload-locations.patch
@@ -0,0 +1,42 @@
+From: Guilhem Moulin <guilhem@debian.org>
+Date: Sun, 13 Mar 2022 00:08:19 +0100
+Subject: Fix autoload locations
+
+Snippets generated with `phpabtpl --suggest bacon/bacon-qr-code` and
+`phpabtpl --suggest GuzzleHttp`.
+
+Forwarded: not-needed
+Bug-Debian: https://bugs.debian.org/1040705
+---
+ program/actions/contacts/qrcode.php | 3 +++
+ program/include/rcmail_oauth.php | 3 +++
+ 2 files changed, 6 insertions(+)
+
+diff --git a/program/actions/contacts/qrcode.php b/program/actions/contacts/qrcode.php
+index 0ece569..87c31da 100644
+--- a/program/actions/contacts/qrcode.php
++++ b/program/actions/contacts/qrcode.php
+@@ -17,6 +17,9 @@
+ +-----------------------------------------------------------------------+
+ */
+
++if (stream_resolve_include_path('Bacon/BaconQrCode/autoload.php'))
++ include_once('Bacon/BaconQrCode/autoload.php');
++
+ class rcmail_action_contacts_qrcode extends rcmail_action_contacts_index
+ {
+ protected static $mode = self::MODE_HTTP;
+diff --git a/program/include/rcmail_oauth.php b/program/include/rcmail_oauth.php
+index b3f88fb..fc592ee 100644
+--- a/program/include/rcmail_oauth.php
++++ b/program/include/rcmail_oauth.php
+@@ -17,6 +17,9 @@
+ +-----------------------------------------------------------------------+
+ */
+
++if (stream_resolve_include_path('GuzzleHttp/autoload.php'))
++ include_once 'GuzzleHttp/autoload.php';
++
+ use GuzzleHttp\Client;
+ use GuzzleHttp\MessageFormatter;
+ use GuzzleHttp\Exception\RequestException;
diff --git a/debian/patches/fix-install-path.patch b/debian/patches/fix-install-path.patch
index 0598f0d69..5bb357d47 100644
--- a/debian/patches/fix-install-path.patch
+++ b/debian/patches/fix-install-path.patch
@@ -166,7 +166,7 @@ index e7dd5cb..3c07a81 100755
require_once INSTALL_PATH.'program/include/clisetup.php';
diff --git a/bin/update.sh b/bin/update.sh
-index a031304..f468f57 100755
+index 13f0846..c123385 100755
--- a/bin/update.sh
+++ b/bin/update.sh
@@ -18,7 +18,7 @@
@@ -218,10 +218,10 @@ index 1145e25..eb4a0ee 100644
require INSTALL_PATH . 'program/include/iniset.php';
diff --git a/program/include/iniset.php b/program/include/iniset.php
-index 9faecad..1778953 100644
+index 6f9946e..c4ab39f 100644
--- a/program/include/iniset.php
+++ b/program/include/iniset.php
-@@ -28,7 +28,7 @@ define('RCMAIL_VERSION', '1.6.1');
+@@ -28,7 +28,7 @@ define('RCMAIL_VERSION', '1.6-git');
define('RCMAIL_START', microtime(true));
if (!defined('INSTALL_PATH')) {
diff --git a/debian/patches/fix-upstream-test-suite.patch b/debian/patches/fix-upstream-test-suite.patch
index d1fd9966b..c4bec6e37 100644
--- a/debian/patches/fix-upstream-test-suite.patch
+++ b/debian/patches/fix-upstream-test-suite.patch
@@ -2,21 +2,16 @@ From: Guilhem Moulin <guilhem@debian.org>
Date: Tue, 20 Dec 2022 17:44:16 +0100
Subject: Fix upstream's test suite
-In `$rcmail->format_date($date, 'x')`'s current output the 12-hour clock
-time is followed by a narrow non-breaking space (U+202F) not a normal
-ASCII space (0x20).
-
Also, in our environment phpunit(1) resides in /usr/bin not vendor/bin.
---
tests/Rcmail/OutputHtml.php | 4 ++--
- tests/Rcmail/Rcmail.php | 2 +-
- 2 files changed, 3 insertions(+), 3 deletions(-)
+ 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/Rcmail/OutputHtml.php b/tests/Rcmail/OutputHtml.php
-index 9c89183..d97f5e5 100644
+index 88b61c5..b38fbfe 100644
--- a/tests/Rcmail/OutputHtml.php
+++ b/tests/Rcmail/OutputHtml.php
-@@ -381,7 +381,7 @@ class Rcmail_RcmailOutputHtml extends PHPUnit\Framework\TestCase
+@@ -383,7 +383,7 @@ class Rcmail_RcmailOutputHtml extends PHPUnit\Framework\TestCase
$rcmail = rcube::get_instance();
$output = new rcmail_output_html();
@@ -25,7 +20,7 @@ index 9c89183..d97f5e5 100644
}
/**
-@@ -404,7 +404,7 @@ class Rcmail_RcmailOutputHtml extends PHPUnit\Framework\TestCase
+@@ -406,7 +406,7 @@ class Rcmail_RcmailOutputHtml extends PHPUnit\Framework\TestCase
$output = new rcmail_output_html();
$expected = '<form name="rcmqsearchform" onsubmit="rcmail.command(\'search\'); return false"'
@@ -34,16 +29,3 @@ index 9c89183..d97f5e5 100644
. '<input name="_q" class="no-bs" id="rcmqsearchbox" placeholder="Search..." type="text"></form>';
$this->assertSame($expected, $output->search_form([]));
-diff --git a/tests/Rcmail/Rcmail.php b/tests/Rcmail/Rcmail.php
-index 8feb8a7..1466f74 100644
---- a/tests/Rcmail/Rcmail.php
-+++ b/tests/Rcmail/Rcmail.php
-@@ -279,7 +279,7 @@ class Rcmail_Rcmail extends ActionTestCase
- $this->assertSame(' Mon', $rcmail->format_date($date, ' D'));
- $this->assertSame('D Monday', $rcmail->format_date($date, '\\D l'));
- $this->assertSame('Jun June', $rcmail->format_date($date, 'M F'));
-- $this->assertSame('6/1/20, 12:20 PM', $rcmail->format_date($date, 'x'));
-+ $this->assertSame('6/1/20, 12:20 PM', $rcmail->format_date($date, 'x'));
- $this->assertSame('1591014030', $rcmail->format_date($date, 'U'));
- $this->assertSame('2020-06-01T12:20:30+00:00', $rcmail->format_date($date, 'c'));
- }
diff --git a/debian/patches/map-sqlite3-to-sqlite.patch b/debian/patches/map-sqlite3-to-sqlite.patch
index 967afbdc7..6a14735c4 100644
--- a/debian/patches/map-sqlite3-to-sqlite.patch
+++ b/debian/patches/map-sqlite3-to-sqlite.patch
@@ -9,7 +9,7 @@ Forwarded: not-needed
1 file changed, 1 insertion(+)
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
-index 41ee42f..4b64482 100644
+index 394a59b..9c0b5d9 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -81,6 +81,7 @@ class rcube_db
diff --git a/debian/patches/mark-flaky-tests-as-such.patch b/debian/patches/mark-flaky-tests-as-such.patch
index ca048da09..6491e949e 100644
--- a/debian/patches/mark-flaky-tests-as-such.patch
+++ b/debian/patches/mark-flaky-tests-as-such.patch
@@ -11,7 +11,7 @@ tests.
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/tests/Actions/Contacts/Qrcode.php b/tests/Actions/Contacts/Qrcode.php
-index 7ac4444..a9c7b0a 100644
+index 2b367bb..bfb71e8 100644
--- a/tests/Actions/Contacts/Qrcode.php
+++ b/tests/Actions/Contacts/Qrcode.php
@@ -9,6 +9,7 @@ class Actions_Contacts_Qrcode extends ActionTestCase
diff --git a/debian/patches/series b/debian/patches/series
index 1ed102756..55fd5ddf4 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -12,7 +12,7 @@ update-jsdeps.patch
use-system-JQueryUI.patch
rename-python-to-python3.patch
adjust-test-environment-for-dep8.patch
-fix-BaconQrCode-autoload-location.patch
+fix-autoload-locations.patch
mark-flaky-tests-as-such.patch
dont-force-set-session.gc_probability=1.patch
fix-upstream-test-suite.patch
diff --git a/debian/patches/update-script.patch b/debian/patches/update-script.patch
index 1f68be407..65c49e4ac 100644
--- a/debian/patches/update-script.patch
+++ b/debian/patches/update-script.patch
@@ -9,7 +9,7 @@ Forwarded: not-needed
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/bin/update.sh b/bin/update.sh
-index f468f57..a35dce3 100755
+index c123385..de6c10c 100755
--- a/bin/update.sh
+++ b/bin/update.sh
@@ -19,6 +19,7 @@
@@ -24,9 +24,9 @@ index f468f57..a35dce3 100755
if (!empty($opts['accept']) || strtolower($input) == 'y') {
$error = $written = false;
-- echo ". backing up the current config file(s)...\n";
+- echo "- backing up the current config file(s)...\n";
+ if (!DEBIAN_PKG) {
-+ echo ". backing up the current config file(s)...\n";
++ echo "- backing up the current config file(s)...\n";
- foreach (['config', 'main', 'db'] as $file) {
- if (file_exists(RCMAIL_CONFIG_DIR . '/' . $file . '.inc.php')) {
@@ -43,8 +43,8 @@ index f468f57..a35dce3 100755
if (!$error) {
$RCI->merge_config();
-- echo ". writing " . RCMAIL_CONFIG_DIR . "/config.inc.php...\n";
-+ echo ". writing " . RCMAIL_CONFIG_DIR . "/config.inc.php".(DEBIAN_PKG ? ".dpkg-new" : "")."...\n";
+- echo "- writing " . RCMAIL_CONFIG_DIR . "/config.inc.php...\n";
++ echo "- writing " . RCMAIL_CONFIG_DIR . "/config.inc.php".(DEBIAN_PKG ? ".dpkg-new" : "")."...\n";
$written = $RCI->save_configfile($RCI->create_config(false));
}
@@ -67,7 +67,7 @@ index f468f57..a35dce3 100755
$success = rcmail_utils::db_update(INSTALL_PATH . 'SQL', 'roundcube', $opts['version'], ['errors' => true]);
}
diff --git a/program/include/rcmail_install.php b/program/include/rcmail_install.php
-index 95cef13..dc42877 100644
+index 13fd40e..e8278f8 100644
--- a/program/include/rcmail_install.php
+++ b/program/include/rcmail_install.php
@@ -18,6 +18,9 @@
diff --git a/debian/patches/use-enchant.patch b/debian/patches/use-enchant.patch
index 96bcf65a0..b4b8f658a 100644
--- a/debian/patches/use-enchant.patch
+++ b/debian/patches/use-enchant.patch
@@ -10,10 +10,10 @@ Forwarded: not-needed
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
-index 12fecca..77e843f 100644
+index 85481d2..00f1a8d 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
-@@ -938,7 +938,8 @@ $config['spellcheck_dictionary'] = false;
+@@ -939,7 +939,8 @@ $config['spellcheck_dictionary'] = false;
// Since Google shut down their public spell checking service, the default settings
// connect to http://spell.roundcube.net which is a hosted service provided by Roundcube.
// You can connect to any other googie-compliant service by setting 'spellcheck_uri' accordingly.
diff --git a/debian/patches/use-system-JQueryUI.patch b/debian/patches/use-system-JQueryUI.patch
index ee4cd3a52..27277db9d 100644
--- a/debian/patches/use-system-JQueryUI.patch
+++ b/debian/patches/use-system-JQueryUI.patch
@@ -15,7 +15,7 @@ Forwarded: not-needed
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php
-index a0bfda3..a98afba 100644
+index 7628539..2f23761 100644
--- a/plugins/jqueryui/jqueryui.php
+++ b/plugins/jqueryui/jqueryui.php
@@ -40,6 +40,7 @@ class jqueryui extends rcube_plugin
diff --git a/debian/po/ro.po b/debian/po/ro.po
new file mode 100644
index 000000000..e60a1b084
--- /dev/null
+++ b/debian/po/ro.po
@@ -0,0 +1,143 @@
+# Mesajele în limba română pentru pachetul roundcube.
+# Romanian translation of roundcube.
+# Copyright © 2023 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the roundcube package.
+#
+# Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>, 2023.
+#
+# Cronologia traducerii fișierului „roundcube”:
+# Traducerea inițială, făcută de R-GC, pentru versiunea roundcube 1.6.1+dfsg-1(2009-02-15).
+# Actualizare a traducerii pentru versiunea Y, făcută de X, Y(anul).
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: roundcube 1.6.1+dfsg-1\n"
+"Report-Msgid-Bugs-To: roundcube@packages.debian.org\n"
+"POT-Creation-Date: 2009-02-15 17:05+0100\n"
+"PO-Revision-Date: 2023-03-19 12:55+0100\n"
+"Last-Translator: Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>\n"
+"Language-Team: Romanian <debian-l10n-romanian@lists.debian.org>\n"
+"Language: ro\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && "
+"n%100<=19) ? 1 : 2);\n"
+"X-Bugs: Report translation errors to the Language-Team address.\n"
+"X-Generator: Poedit 3.2.2\n"
+
+#. Type: multiselect
+#. Choices
+#: ../templates:2001
+msgid "apache2"
+msgstr "apache2"
+
+#. Type: multiselect
+#. Choices
+#: ../templates:2001
+msgid "lighttpd"
+msgstr "lighttpd"
+
+#. Type: multiselect
+#. Description
+#: ../templates:2002
+msgid "Web server(s) to configure automatically:"
+msgstr "Servere web de reconfigurat automat:"
+
+#. Type: multiselect
+#. Description
+#: ../templates:2002
+msgid ""
+"RoundCube supports any web server supported by PHP, however only Apache 2 and "
+"lighttpd can be configured automatically."
+msgstr ""
+"RoundCube acceptă orice server web acceptat de PHP, însă numai Apache 2 și "
+"lighttpd pot fi configurate automat."
+
+#. Type: multiselect
+#. Description
+#: ../templates:2002
+msgid ""
+"Please select the web server(s) that should be configured automatically for "
+"RoundCube."
+msgstr ""
+"Selectați serverul(ele) web care ar trebui să fie configurat(e) automat pentru "
+"RoundCube."
+
+#. Type: boolean
+#. Description
+#: ../templates:3001
+msgid "Should the webserver(s) be restarted now?"
+msgstr "Doriți să fie repornit(e) serverul(ele) web acum?"
+
+#. Type: boolean
+#. Description
+#: ../templates:3001
+msgid ""
+"In order to activate the new configuration, the reconfigured web server(s) have "
+"to be restarted."
+msgstr ""
+"Pentru a activa noua configurație, serverul(ele) web reconfigurat(e) trebuie să "
+"fie repornit(e)."
+
+#. Type: string
+#. Description
+#: ../templates:4001
+msgid "IMAP server(s) used with RoundCube:"
+msgstr "Server(e) IMAP folosit(e) cu RoundCube:"
+
+#. Type: string
+#. Description
+#: ../templates:4001
+msgid "Please select the IMAP server(s) that should be used with RoundCube."
+msgstr ""
+"Selectați serverul(ele) IMAP care ar trebui să fie folosit(e) cu RoundCube."
+
+#. Type: string
+#. Description
+#: ../templates:4001
+msgid ""
+"If this is left blank, a text box will be displayed at login. Entering a space-"
+"separated list of hosts will display a pull-down menu. Entering a single host "
+"will enforce using this host."
+msgstr ""
+"Dacă acesta este lăsat necompletat, va fi afișată o casetă de text la "
+"conectare. Introducerea unei liste de gazde separate prin spații va afișa un "
+"meniu derulant. Introducerea unei singure gazde va impune utilizarea acestei "
+"gazde."
+
+#. Type: string
+#. Description
+#: ../templates:4001
+msgid "To use SSL connections, please enter host names as 'ssl://hostname:993'."
+msgstr ""
+"Pentru a utiliza conexiunile SSL, introduceți numele gazdei ca „ssl://nume-"
+"gazdă:993”."
+
+#. Type: select
+#. Description
+#: ../templates:5001
+msgid "Default language:"
+msgstr "Limba implicită:"
+
+#. Type: select
+#. Description
+#: ../templates:5001
+msgid "Please choose the default language for RoundCube."
+msgstr "Alegeți limba implicită pentru RoundCube."
+
+#. Type: select
+#. Description
+#: ../templates:5001
+msgid "This choice can be overridden by individual users in their preferences."
+msgstr "Această alegere poate fi înlocuită de utilizatori în preferințele lor."
+
+#. Type: select
+#. Description
+#: ../templates:5001
+msgid ""
+"However, the default language will be used for the login screen and the first "
+"connection of users."
+msgstr ""
+"Cu toate acestea, limba implicită va fi folosită pentru ecranul de conectare și "
+"prima conexiune a utilizatorilor."
diff --git a/debian/roundcube-core.cron.d b/debian/roundcube-core.cron.d
index e8003a53d..a280a05d3 100644
--- a/debian/roundcube-core.cron.d
+++ b/debian/roundcube-core.cron.d
@@ -4,4 +4,4 @@
0 5 * * * www-data test -d /run/systemd/system || /usr/share/roundcube/bin/cleandb.sh >/dev/null
# Purge expired sessions, caches and tempfiles
-* 5 * * * www-data test -d /run/systemd/system || /usr/share/roundcube/bin/gc.sh
+5,35 * * * * www-data test -d /run/systemd/system || /usr/share/roundcube/bin/gc.sh
diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml
index 70954ecc9..6ce371128 100644
--- a/debian/salsa-ci.yml
+++ b/debian/salsa-ci.yml
@@ -3,5 +3,6 @@ include:
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
variables:
+ RELEASE: 'bookworm'
# install suitable RDBMS before running piuparts (workaround for #1015732)
SALSA_CI_PIUPARTS_PRE_INSTALL_SCRIPT: 'debian/salsa-ci/pre_install_database-server'
diff --git a/debian/sql/mysql/1.3.0-1 b/debian/sql/mysql/1.3.0-1
index 51f587132..dd3998932 100644
--- a/debian/sql/mysql/1.3.0-1
+++ b/debian/sql/mysql/1.3.0-1
@@ -11,7 +11,8 @@ ALTER TABLE `session` MODIFY `ip` varchar(40) NOT NULL;
UPDATE `system` SET `value` = '2016100900' WHERE `name` = 'roundcube-version';
/* 2016112200 */
-ALTER TABLE `dictionary` ADD COLUMN `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; -- redundant, for compat. with Galera Cluster
+-- redundant, for compat. with Galera Cluster
+ALTER TABLE `dictionary` ADD COLUMN `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
DROP TABLE `cache`;
DROP TABLE `cache_shared`;
diff --git a/index.php b/index.php
index 87de8900e..0e51d3523 100644
--- a/index.php
+++ b/index.php
@@ -2,7 +2,7 @@
/**
+-------------------------------------------------------------------------+
| Roundcube Webmail IMAP Client |
- | Version 1.6.1 |
+ | Version 1.6-git |
| |
| Copyright (C) The Roundcube Dev Team |
| |
diff --git a/plugins/attachment_reminder/attachment_reminder.js b/plugins/attachment_reminder/attachment_reminder.js
index 722b7e75a..633922a51 100644
--- a/plugins/attachment_reminder/attachment_reminder.js
+++ b/plugins/attachment_reminder/attachment_reminder.js
@@ -17,14 +17,16 @@
function rcmail_get_compose_message()
{
- var msg;
+ var msg = rcmail.editor.get_content({ nosig: true });
- if (window.tinyMCE && (ed = tinyMCE.get(rcmail.env.composebody))) {
- msg = ed.getContent();
- msg = msg.replace(/<blockquote[^>]*>(.|[\r\n])*<\/blockquote>/gmi, '');
+ if (rcmail.editor.is_html()) {
+ // Remove quoted content, all HTML tags, and some entities
+ msg = msg.replace(/<blockquote[^>]*>(.|[\r\n])*<\/blockquote>/gmi, '')
+ .replace(/<[^>]+>/gm, ' ')
+ .replace(/ /g, ' ');
}
else {
- msg = $('#' + rcmail.env.composebody).val();
+ // Remove quoted content
msg = msg.replace(/^>.*$/gmi, '');
}
diff --git a/plugins/help/config.inc.php.dist b/plugins/help/config.inc.php.dist
index d246cfc5f..4ac5864b5 100644
--- a/plugins/help/config.inc.php.dist
+++ b/plugins/help/config.inc.php.dist
@@ -3,7 +3,7 @@
// Help content iframe source
// %l will be replaced by the language code resolved using the 'help_language_map' option
// If you are serving roundcube via https, then change this URL to https also.
-$config['help_source'] = 'http://docs.roundcube.net/doc/help/1.1/%l/';
+$config['help_source'] = 'https://docs.roundcube.net/doc/help/1.1/%l/';
// Map task/action combinations to deep-links
// Use '<task>/<action>' or only '<task>' strings as keys
diff --git a/plugins/help/help.js b/plugins/help/help.js
index 7928b2017..6792d2006 100644
--- a/plugins/help/help.js
+++ b/plugins/help/help.js
@@ -55,7 +55,7 @@ if (window.rcmail) {
show_help_content(rcmail.env.action);
}
}
- catch (e) { /* ignore */}
+ catch (e) { /* ignore */ }
}
});
}
diff --git a/plugins/jqueryui/composer.json b/plugins/jqueryui/composer.json
index 10cf660e8..97ed99898 100644
--- a/plugins/jqueryui/composer.json
+++ b/plugins/jqueryui/composer.json
@@ -3,7 +3,7 @@
"type": "roundcube-plugin",
"description": "Plugin adds the complete jQuery-UI library including the smoothness theme to Roundcube. This allows other plugins to use jQuery-UI without having to load their own version. The benefit of using one central jQuery-UI is that we wont run into problems of conflicting jQuery libraries being loaded. All plugins that want to use jQuery-UI should use this plugin as a requirement.",
"license": "GPL-3.0-or-later",
- "version": "1.13.1",
+ "version": "1.13.2",
"authors": [
{
"name": "Thomas Bruederli",
diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php
index a98afba62..2f23761dd 100644
--- a/plugins/jqueryui/jqueryui.php
+++ b/plugins/jqueryui/jqueryui.php
@@ -5,7 +5,7 @@
*
* Provide the jQuery UI library with according themes.
*
- * @version 1.13.1
+ * @version 1.13.2
* @author Cor Bosman <roundcube@wa.ter.net>
* @author Thomas Bruederli <roundcube@gmail.com>
* @author Aleksander Machniak <alec@alec.pl>
@@ -14,7 +14,7 @@
class jqueryui extends rcube_plugin
{
public $noajax = true;
- public $version = '1.13.1';
+ public $version = '1.13.2';
private static $features = [];
private static $ui_theme;
diff --git a/plugins/jqueryui/themes/elastic/jquery-ui.css b/plugins/jqueryui/themes/elastic/jquery-ui.css
index 819a49299..c3b98cc44 100644
--- a/plugins/jqueryui/themes/elastic/jquery-ui.css
+++ b/plugins/jqueryui/themes/elastic/jquery-ui.css
@@ -1,4 +1,4 @@
-/*! jQuery UI - v1.13.1 - 2022-01-20
+/*! jQuery UI - v1.13.2 - 2022-07-14
* http://jqueryui.com
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
index 549fbf699..9a16d1f61 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -676,7 +676,7 @@ class rcube_sieve_engine
$notifytargets = rcube_utils::get_input_value('_action_notifytarget', rcube_utils::INPUT_POST, true);
$notifyoptions = rcube_utils::get_input_value('_action_notifyoption', rcube_utils::INPUT_POST, true);
$notifymessages = rcube_utils::get_input_value('_action_notifymessage', rcube_utils::INPUT_POST, true);
- $notifyfrom = rcube_utils::get_input_value('_action_notifyfrom', rcube_utils::INPUT_POST);
+ $notifyfrom = rcube_utils::get_input_value('_action_notifyfrom', rcube_utils::INPUT_POST, true);
$notifyimp = rcube_utils::get_input_value('_action_notifyimportance', rcube_utils::INPUT_POST);
$addheader_name = rcube_utils::get_input_value('_action_addheader_name', rcube_utils::INPUT_POST);
$addheader_value = rcube_utils::get_input_value('_action_addheader_value', rcube_utils::INPUT_POST, true);
@@ -1174,28 +1174,7 @@ class rcube_sieve_engine
if (!empty($this->form['actions'][$i]['from'])) {
// According to RFC5230 the :from string must specify a valid [RFC2822] mailbox-list
- // we'll try to extract addresses and validate them separately
- $from = rcube_mime::decode_address_list($this->form['actions'][$i]['from'], null, true, RCUBE_CHARSET);
- foreach ((array) $from as $idx => $addr) {
- if (empty($addr['mailto']) || !rcube_utils::check_email($addr['mailto'])) {
- $this->errors['actions'][$i]['from'] = $this->plugin->gettext('noemailwarning');
- break;
- }
- else {
- $from[$idx] = format_email_recipient($addr['mailto'], $addr['name']);
- }
- }
-
- // Only one address is allowed (at least on cyrus imap)
- if (is_array($from) && count($from) > 1) {
- $this->errors['actions'][$i]['from'] = $this->plugin->gettext('noemailwarning');
- }
-
- // Then we convert it back to RFC2822 format
- if (empty($this->errors['actions'][$i]['from']) && !empty($from)) {
- $this->form['actions'][$i]['from'] = Mail_mimePart::encodeHeader(
- 'From', implode(', ', $from), RCUBE_CHARSET, 'base64', '');
- }
+ $this->action_email_input($i, 'from');
}
if ($this->form['actions'][$i]['reason'] == '') {
@@ -1232,9 +1211,6 @@ class rcube_sieve_engine
if (empty($notifytargets[$idx])) {
$this->errors['actions'][$i]['target'] = $this->plugin->gettext('cannotbeempty');
}
- if (!empty($notifyfrom[$idx]) && !rcube_utils::check_email($notifyfrom[$idx])) {
- $this->errors['actions'][$i]['from'] = $this->plugin->gettext('noemailwarning');
- }
// skip empty options
foreach ((array)$notifyoptions[$idx] as $opt_idx => $opt) {
@@ -1248,6 +1224,12 @@ class rcube_sieve_engine
$this->form['actions'][$i]['message'] = $notifymessages[$idx];
$this->form['actions'][$i]['from'] = $notifyfrom[$idx];
$this->form['actions'][$i]['importance'] = $notifyimp[$idx];
+
+ if (!empty($notifyfrom[$idx]) && stripos($this->form['actions'][$i]['method'], 'mailto:') === 0) {
+ // For mailto method :from string must specify a valid [RFC2822] mailbox-list
+ $this->action_email_input($i, 'from');
+ }
+
break;
}
@@ -2970,13 +2952,15 @@ class rcube_sieve_engine
$this->sieve->load($user_script);
- foreach ($this->sieve->script->as_array() as $rules) {
- foreach ($rules['actions'] as $action) {
- if ($action['type'] == 'include' && empty($action['global'])) {
- $name = preg_replace($filename_regex, '', $action['target']);
- // make sure the script exist
- if (in_array($name, $this->list)) {
- $this->active[] = $name;
+ if (!empty($this->sieve->script)) {
+ foreach ($this->sieve->script->as_array() as $rules) {
+ foreach ($rules['actions'] as $action) {
+ if ($action['type'] == 'include' && empty($action['global'])) {
+ $name = preg_replace($filename_regex, '', $action['target']);
+ // make sure the script exist
+ if (in_array($name, $this->list)) {
+ $this->active[] = $name;
+ }
}
}
}
@@ -3494,4 +3478,38 @@ class rcube_sieve_engine
return $script_name;
}
+
+ /**
+ * Read email address input, parse it and check validity
+ */
+ protected function action_email_input($i, $field)
+ {
+ // According to RFC5230 the :from string must specify a valid [RFC2822] mailbox-list
+ // we'll try to extract addresses and validate them separately
+ $from = rcube_mime::decode_address_list($this->form['actions'][$i][$field], null, true, RCUBE_CHARSET);
+ foreach ((array) $from as $idx => $addr) {
+ if (empty($addr['mailto']) || !rcube_utils::check_email($addr['mailto'])) {
+ $this->errors['actions'][$i][$field] = $this->plugin->gettext('noemailwarning');
+ break;
+ }
+ else {
+ $from[$idx] = format_email_recipient($addr['mailto'], $addr['name']);
+ }
+ }
+
+ // Only one address is allowed (at least on cyrus imap)
+ if (is_array($from) && count($from) > 1) {
+ $this->errors['actions'][$i][$field] = $this->plugin->gettext('noemailwarning');
+ }
+
+ // Then we convert it back to RFC2822 format
+ if (empty($this->errors['actions'][$i][$field]) && !empty($from)) {
+ $this->form['actions'][$i][$field] = Mail_mimePart::encodeHeader(
+ 'From', implode(', ', $from), RCUBE_CHARSET, 'base64', '');
+
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
index e39b260a6..f69229113 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
@@ -701,21 +701,23 @@ class rcube_sieve_vacation extends rcube_sieve_engine
if ($date_extension) {
$date_value = [];
- foreach ((array) $this->vacation['tests'] as $test) {
- if ($test['test'] == 'currentdate') {
- $idx = $test['type'] == 'value-ge' ? 'start' : 'end';
+ if (!empty($this->vacation['tests'])) {
+ foreach ((array) $this->vacation['tests'] as $test) {
+ if ($test['test'] == 'currentdate') {
+ $idx = $test['type'] == 'value-ge' ? 'start' : 'end';
- if ($test['part'] == 'date') {
- $date_value[$idx]['date'] = $test['arg'];
- }
- else if ($test['part'] == 'iso8601') {
- $date_value[$idx]['datetime'] = $test['arg'];
+ if ($test['part'] == 'date') {
+ $date_value[$idx]['date'] = $test['arg'];
+ }
+ else if ($test['part'] == 'iso8601') {
+ $date_value[$idx]['datetime'] = $test['arg'];
+ }
}
}
}
foreach ($date_value as $idx => $value) {
- $$idx = new DateTime($value['datetime'] ?: $value['date'], $timezone);
+ ${$idx} = new DateTime(!empty($value['datetime']) ? $value['datetime'] : $value['date'], $timezone);
}
}
else if ($regex_extension) {
@@ -738,13 +740,13 @@ class rcube_sieve_vacation extends rcube_sieve_engine
'interval' => $interval,
'start' => $start,
'end' => $end,
- 'enabled' => $this->vacation['reason'] && empty($this->vacation['disabled']),
- 'message' => $this->vacation['reason'],
- 'subject' => $this->vacation['subject'],
- 'action' => $this->vacation['action'],
- 'target' => $this->vacation['target'],
- 'addresses' => $this->vacation['addresses'],
- 'from' => $this->vacation['from'],
+ 'enabled' => !empty($this->vacation['reason']) && empty($this->vacation['disabled']),
+ 'message' => isset($this->vacation['reason']) ? $this->vacation['reason'] : null,
+ 'subject' => isset($this->vacation['subject']) ? $this->vacation['subject'] : null,
+ 'action' => isset($this->vacation['action']) ? $this->vacation['action'] : null,
+ 'target' => isset($this->vacation['target']) ? $this->vacation['target'] : null,
+ 'addresses' => isset($this->vacation['addresses']) ? $this->vacation['addresses'] : null,
+ 'from' => isset($this->vacation['from']) ? $this->vacation['from'] : null,
];
return $vacation;
diff --git a/plugins/markasjunk/drivers/cmd_learn.php b/plugins/markasjunk/drivers/cmd_learn.php
index 83ee0abae..c3ebf9f79 100644
--- a/plugins/markasjunk/drivers/cmd_learn.php
+++ b/plugins/markasjunk/drivers/cmd_learn.php
@@ -3,7 +3,7 @@
/**
* Command line learn driver
*
- * @version 3.0
+ * @version 3.1
*
* @author Philip Weir
* Patched by Julien Vehent to support DSPAM
@@ -49,14 +49,19 @@ class markasjunk_cmd_learn
return;
}
+ if (strpos($command, '%h') !== false) {
+ preg_match_all('/%h:([\w_-]+)/', $command, $header_names, PREG_SET_ORDER);
+ $header_names = array_column($header_names, 1);
+ }
+
// backwards compatibility %xds removed in markasjunk v1.12
$command = str_replace('%xds', '%h:x-dspam-signature', $command);
- $command = str_replace('%u', $_SESSION['username'], $command);
- $command = str_replace('%l', $rcube->user->get_username('local'), $command);
- $command = str_replace('%d', $rcube->user->get_username('domain'), $command);
+ $command = str_replace('%u', escapeshellarg($_SESSION['username']), $command);
+ $command = str_replace('%l', escapeshellarg($rcube->user->get_username('local')), $command);
+ $command = str_replace('%d', escapeshellarg($rcube->user->get_username('domain')), $command);
if (strpos($command, '%i') !== false) {
$identity = $rcube->user->get_identity();
- $command = str_replace('%i', $identity['email'], $command);
+ $command = str_replace('%i', escapeshellarg($identity['email']), $command);
}
foreach ($uids as $uid) {
@@ -68,24 +73,24 @@ class markasjunk_cmd_learn
$tmp_command = str_replace('%s', escapeshellarg($message->sender['mailto']), $tmp_command);
}
- if (strpos($command, '%h') !== false) {
+ if (!empty($header_names)) {
$storage = $rcube->get_storage();
$storage->check_connection();
- $storage->conn->select($src_mbox);
+ $headers = $storage->conn->fetchHeader($src_mbox, $uid, true, false, $header_names);
- preg_match_all('/%h:([\w_-]+)/', $tmp_command, $header_names, PREG_SET_ORDER);
foreach ($header_names as $header) {
$val = null;
- if ($msg = $storage->conn->fetchHeader($src_mbox, $uid, true, false, [$header[1]])) {
- $val = !empty($msg->{$header[1]}) ? $msg->{$header[1]} : $msg->others[$header[1]];
+ if ($headers) {
+ $val = $headers->get($header);
+ $val = is_array($val) ? array_first($val) : $val;
}
if (!empty($val)) {
- $tmp_command = str_replace($header[0], escapeshellarg($val), $tmp_command);
+ $tmp_command = str_replace('%h:' . $header, escapeshellarg($val), $tmp_command);
}
else {
if ($debug) {
- rcube::write_log('markasjunk', 'header ' . $header[1] . ' not found in message ' . $src_mbox . '/' . $uid);
+ rcube::write_log('markasjunk', "header {$header} not found in message {$src_mbox}/{$uid}");
}
continue 2;
diff --git a/plugins/markasjunk/drivers/email_learn.php b/plugins/markasjunk/drivers/email_learn.php
index 6d41d0406..6b1d83298 100644
--- a/plugins/markasjunk/drivers/email_learn.php
+++ b/plugins/markasjunk/drivers/email_learn.php
@@ -49,17 +49,16 @@ class markasjunk_email_learn
$product = $this->rcube->config->get('product_name');
$temp_dir = unslashify($this->rcube->config->get('temp_dir'));
- $mailto = $this->rcube->config->get($spam ? 'markasjunk_email_spam' : 'markasjunk_email_ham');
- $mailto = $this->_parse_vars($mailto, $spam, $from);
+ $subject = (string) $this->rcube->config->get('markasjunk_email_subject');
+ $mailto = (string) $this->rcube->config->get($spam ? 'markasjunk_email_spam' : 'markasjunk_email_ham');
+ $subject = $this->_parse_vars($subject, $spam, $from);
+ $mailto = $this->_parse_vars($mailto, $spam, $from);
// no address to send to, exit
if (!$mailto) {
return;
}
- $subject = $this->rcube->config->get('markasjunk_email_subject');
- $subject = $this->_parse_vars($subject, $spam, $from);
-
foreach ($uids as $i => $uid) {
$MESSAGE = new rcube_message($uid);
$message_file = null;
diff --git a/plugins/markasjunk/drivers/sa_blacklist.php b/plugins/markasjunk/drivers/sa_blacklist.php
index 5bd0f6a32..f877c9b0f 100644
--- a/plugins/markasjunk/drivers/sa_blacklist.php
+++ b/plugins/markasjunk/drivers/sa_blacklist.php
@@ -52,6 +52,10 @@ class markasjunk_sa_blacklist
$this->sa_preference_field = $rcube->config->get('sauserprefs_sql_preference_field');
$this->sa_value_field = $rcube->config->get('sauserprefs_sql_value_field');
+ // SAv4 compatibility
+ $blocklist_pref_name = $rcube->config->get('sauserprefs_sav4', false) ? "blocklist_from" : "blacklist_from";
+ $welcomelist_pref_name = $rcube->config->get('sauserprefs_sav4', false) ? "welcomelist_from" : "whitelist_from";
+
$identity = $rcube->user->get_identity();
$identity = $identity['email'];
@@ -97,21 +101,21 @@ class markasjunk_sa_blacklist
}
if ($spam) {
- // delete any whitelisting for this address
+ // delete any welcomelisting for this address
$db->query(
"DELETE FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? "
. "AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?",
$this->sa_user,
- 'whitelist_from',
+ $welcomelist_pref_name,
$email
);
- // check address is not already blacklisted
+ // check address is not already blocklisted
$sql_result = $db->query(
"SELECT `value` FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? "
. "AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?",
$this->sa_user,
- 'blacklist_from',
+ $blocklist_pref_name,
$email
);
@@ -120,31 +124,31 @@ class markasjunk_sa_blacklist
"INSERT INTO `{$this->sa_table}` (`{$this->sa_username_field}`, `{$this->sa_preference_field}`, `{$this->sa_value_field}`)"
. " VALUES (?, ?, ?)",
$this->sa_user,
- 'blacklist_from',
+ $blocklist_pref_name,
$email
);
if ($debug) {
- rcube::write_log('markasjunk', $this->sa_user . ' blacklist ' . $email);
+ rcube::write_log('markasjunk', $this->sa_user . ' blocklist ' . $email);
}
}
}
else {
- // delete any blacklisting for this address
+ // delete any blocklisting for this address
$db->query(
"DELETE FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? AND "
. "`{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?",
$this->sa_user,
- 'blacklist_from',
+ $blocklist_pref_name,
$email
);
- // check address is not already whitelisted
+ // check address is not already welcomelisted
$sql_result = $db->query(
"SELECT `value` FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? "
. "AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?",
$this->sa_user,
- 'whitelist_from',
+ $welcomelist_pref_name,
$email
);
@@ -153,11 +157,11 @@ class markasjunk_sa_blacklist
"INSERT INTO `{$this->sa_table}` (`{$this->sa_username_field}`, `{$this->sa_preference_field}`, `{$this->sa_value_field}`)"
. " VALUES (?, ?, ?)",
$this->sa_user,
- 'whitelist_from',
+ $welcomelist_pref_name,
$email);
if ($debug) {
- rcube::write_log('markasjunk', $this->sa_user . ' whitelist ' . $email);
+ rcube::write_log('markasjunk', $this->sa_user . ' welcomelist ' . $email);
}
}
}
diff --git a/plugins/newmail_notifier/newmail_notifier.js b/plugins/newmail_notifier/newmail_notifier.js
index 49bd7c3c6..e719518d5 100644
--- a/plugins/newmail_notifier/newmail_notifier.js
+++ b/plugins/newmail_notifier/newmail_notifier.js
@@ -84,25 +84,13 @@ function newmail_notifier_basic()
// Sound notification
function newmail_notifier_sound()
{
- var elem, src = rcmail.assets_path('plugins/newmail_notifier/sound'),
- plugin = navigator.mimeTypes ? navigator.mimeTypes['audio/mp3'] : {};
+ var src = rcmail.assets_path('plugins/newmail_notifier/sound');
- // Internet Explorer does not support wav files,
- // support in other browsers depends on enabled plugins,
- // so we use wav as a fallback
- src += bw.ie || (plugin && plugin.enabledPlugin) ? '.mp3' : '.wav';
-
- // HTML5
- try {
- elem = $('<audio>').attr('src', src);
- elem.get(0).play();
- }
- // old method
- catch (e) {
- elem = $('<embed id="sound" src="' + src + '" hidden=true autostart=true loop=false />');
- elem.appendTo($('body'));
- setTimeout("$('#sound').remove()", 5000);
- }
+ (new Audio(src + '.mp3')).play()
+ .catch(function() {
+ // fallback to the wav format
+ (new Audio(src + '.wav')).play();
+ });
}
// Desktop notification
diff --git a/plugins/password/drivers/ldap_simple.php b/plugins/password/drivers/ldap_simple.php
index 04279fca6..53f66e4cc 100644
--- a/plugins/password/drivers/ldap_simple.php
+++ b/plugins/password/drivers/ldap_simple.php
@@ -119,11 +119,12 @@ class rcube_ldap_simple_password
$this->debug = $rcmail->config->get('ldap_debug');
$ldap_host = $rcmail->config->get('password_ldap_host', 'localhost');
$ldap_port = $rcmail->config->get('password_ldap_port', '389');
+ $ldap_uri = $this->_host2uri($ldap_host, $ldap_port);
- $this->_debug("C: Connect to $ldap_host:$ldap_port");
+ $this->_debug("C: Connect [{$ldap_uri}]");
// Connect
- if (!$ds = ldap_connect($ldap_host, $ldap_port)) {
+ if (!($ds = ldap_connect($ldap_uri))) {
$this->_debug("S: NOT OK");
rcube::raise_error([
@@ -290,4 +291,24 @@ class rcube_ldap_simple_password
rcube::write_log('ldap', $str);
}
}
+
+ /**
+ * Convert LDAP host/port into URI
+ */
+ private static function _host2uri($host, $port = null)
+ {
+ if (stripos($host, 'ldapi://') === 0) {
+ return $host;
+ }
+
+ if (strpos($host, '://') === false) {
+ $host = ($port == 636 ? 'ldaps' : 'ldap') . '://' . $host;
+ }
+
+ if ($port && !preg_match('/:[0-9]+$/', $host)) {
+ $host .= ':' . $port;
+ }
+
+ return $host;
+ }
}
diff --git a/plugins/reconnect/composer.json b/plugins/reconnect/composer.json
index 23faaa6a6..c7a990bc8 100644
--- a/plugins/reconnect/composer.json
+++ b/plugins/reconnect/composer.json
@@ -3,7 +3,7 @@
"type": "roundcube-plugin",
"description": "Reconnects to server for several attempts.",
"license": "GPL-3.0-or-later",
- "version": "0.1",
+ "version": "0.2",
"authors": [
{
"name": "Sandro Knauß",
diff --git a/plugins/reconnect/reconnect.php b/plugins/reconnect/reconnect.php
index ecc98ada1..11050dd22 100644
--- a/plugins/reconnect/reconnect.php
+++ b/plugins/reconnect/reconnect.php
@@ -1,16 +1,27 @@
<?php
/**
- * RoundCube Reconnect Plugin
+ * Roundcube Reconnect Plugin
*
- * @version 0.1
+ * @version 0.2
* @author Sandro Knauß <hefee@debian.org>
* @license GPLv3+
*/
class reconnect extends rcube_plugin
{
- private $max_attempts;
+ private $imap_max_attempts;
+ /**
+ * Plugin initialization
+ */
function init()
+ {
+ $this->add_hook('storage_connect', [$this, 'storage_connect']);
+ }
+
+ /**
+ * Storage_connect hook handler
+ */
+ function storage_connect($args)
{
$rcmail = rcmail::get_instance();
@@ -18,11 +29,6 @@ class reconnect extends rcube_plugin
$this->imap_max_attempts = $rcmail->config->get('reconnect_imap_max_attempts', 5);
- $this->add_hook('storage_connect', array($this, 'storage_connect'));
- }
-
- function storage_connect($args)
- {
$args['retry'] = ($args['attempt'] <= $this->imap_max_attempts);
if ($args['attempt'] == 1) {
@@ -30,6 +36,7 @@ class reconnect extends rcube_plugin
}
$storage = rcmail::get_instance()->get_storage();
+
switch ($storage->get_error_code()) {
case rcube_imap_generic::ERROR_NO:
case rcube_imap_generic::ERROR_BAD:
diff --git a/program/actions/contacts/export.php b/program/actions/contacts/export.php
index 8616578ab..632bdfc58 100644
--- a/program/actions/contacts/export.php
+++ b/program/actions/contacts/export.php
@@ -150,11 +150,11 @@ class rcmail_action_contacts_export extends rcmail_action_contacts_index
$fieldmap = $source ? $source->vcard_map : null;
if (empty($record['vcard'])) {
- $vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
+ $vcard = new rcube_vcard(null, RCUBE_CHARSET, false, $fieldmap);
$vcard->reset();
foreach ($record as $key => $values) {
- list($field, $section) = explode(':', $key);
+ list($field, $section) = rcube_utils::explode(':', $key);
// avoid unwanted casting of DateTime objects to an array
// (same as in rcube_contacts::convert_save_data())
if (is_object($values) && is_a($values, 'DateTime')) {
@@ -163,7 +163,7 @@ class rcmail_action_contacts_export extends rcmail_action_contacts_index
foreach ((array) $values as $value) {
if (is_array($value) || is_a($value, 'DateTime') || @strlen($value)) {
- $vcard->set($field, $value, strtoupper($section));
+ $vcard->set($field, $value, $section ? strtoupper($section) : '');
}
}
}
@@ -176,7 +176,7 @@ class rcmail_action_contacts_export extends rcmail_action_contacts_index
$record['vcard'] = $vcard->export();
}
// patch categories to already existing vcard block
- else if (!empty($record['vcard'])) {
+ else {
$vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
// unset CATEGORIES entry, it might be not up-to-date (#1490277)
diff --git a/program/actions/contacts/import.php b/program/actions/contacts/import.php
index 2c4d25d9d..295d377cc 100644
--- a/program/actions/contacts/import.php
+++ b/program/actions/contacts/import.php
@@ -184,7 +184,7 @@ class rcmail_action_contacts_import extends rcmail_action_contacts_index
if (empty($a_record['name'])) {
$a_record['name'] = rcube_addressbook::compose_display_name($a_record, true);
// Reset it if equals to email address (from compose_display_name())
- if ($a_record['name'] == $a_record['email'][0]) {
+ if ($a_record['name'] == ($a_record['email'][0] ?? null)) {
$a_record['name'] = '';
}
}
diff --git a/program/actions/contacts/qrcode.php b/program/actions/contacts/qrcode.php
index 8ab71e859..87c31da9f 100644
--- a/program/actions/contacts/qrcode.php
+++ b/program/actions/contacts/qrcode.php
@@ -108,7 +108,7 @@ class rcmail_action_contacts_qrcode extends rcmail_action_contacts_index
$renderer = new BaconQrCode\Renderer\ImageRenderer($renderer_style, $renderer_image);
$writer = new BaconQrCode\Writer($renderer);
- return $writer->writeString($data);
+ return $writer->writeString($data, RCUBE_CHARSET);
}
/**
diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php
index d9a87aca3..1d5d33f8c 100644
--- a/program/actions/mail/compose.php
+++ b/program/actions/mail/compose.php
@@ -231,7 +231,7 @@ class rcmail_action_mail_compose extends rcmail_action_mail_index
self::$COMPOSE['reply_uid'] = self::$MESSAGE->context === null ? $msg_uid : null;
if (!empty(self::$COMPOSE['param']['all'])) {
- self::$MESSAGE->reply_all = self::$COMPOSE['param']['all'];
+ self::$COMPOSE['reply_all'] = self::$COMPOSE['param']['all'];
}
}
else {
@@ -1615,7 +1615,7 @@ class rcmail_action_mail_compose extends rcmail_action_mail_index
'onclick' => sprintf(
"return %s.command('insert-response', '%s', this, event)",
rcmail_output::JS_OBJECT_NAME,
- rcube::JQ($response['id']),
+ rcube::JQ($response['id'])
),
],
rcube::Q($response['name'])
diff --git a/program/actions/mail/index.php b/program/actions/mail/index.php
index d142c7a12..41e09bffe 100644
--- a/program/actions/mail/index.php
+++ b/program/actions/mail/index.php
@@ -528,14 +528,14 @@ class rcmail_action_mail_index extends rcmail_action
}
}
else if ($col == 'subject') {
- $cont = trim(rcube_mime::decode_header($header->$col, $header->charset));
+ $cont = trim(rcube_mime::decode_header($header->subject, $header->charset));
if (!$cont) {
$cont = $rcmail->gettext('nosubject');
}
$cont = rcube::SQ($cont);
}
else if ($col == 'size') {
- $cont = self::show_bytes($header->$col);
+ $cont = self::show_bytes($header->size);
}
else if ($col == 'date') {
$cont = $rcmail->format_date($sort_col == 'arrival' ? $header->internaldate : $header->date);
diff --git a/program/actions/mail/mark.php b/program/actions/mail/mark.php
index 4fcfbb534..9029eb5a8 100644
--- a/program/actions/mail/mark.php
+++ b/program/actions/mail/mark.php
@@ -45,8 +45,9 @@ class rcmail_action_mail_mark extends rcmail_action_mail_index
$read_deleted = (bool) $rcmail->config->get('read_when_deleted');
$flag = self::imap_flag($flag);
$old_count = 0;
+ $from = $_POST['_from'] ?? null;
- if ($flag == 'DELETED' && $skip_deleted && (!isset($_POST['_from']) || $_POST['_from'] != 'show')) {
+ if ($flag == 'DELETED' && $skip_deleted && $from != 'show') {
// count messages before changing anything
$old_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
}
@@ -79,7 +80,7 @@ class rcmail_action_mail_mark extends rcmail_action_mail_index
if (!$marked) {
// send error message
- if (empty($_POST['_from']) || $_POST['_from'] != 'show') {
+ if ($from != 'show') {
$rcmail->output->command('list_mailbox');
}
@@ -110,7 +111,7 @@ class rcmail_action_mail_mark extends rcmail_action_mail_index
$rcmail->output->set_env('last_flag', $flag);
}
else if ($flag == 'DELETED' && $skip_deleted) {
- if ($_POST['_from'] == 'show') {
+ if ($from == 'show') {
if ($next = rcube_utils::get_input_value('_next_uid', rcube_utils::INPUT_GPC)) {
$rcmail->output->command('show_message', $next);
}
diff --git a/program/actions/mail/show.php b/program/actions/mail/show.php
index 16be9e023..ff798b817 100644
--- a/program/actions/mail/show.php
+++ b/program/actions/mail/show.php
@@ -320,8 +320,11 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
$dbox = $rcmail->config->get('drafts_mbox');
// the message is not a draft
- if (self::$MESSAGE->context
- || (self::$MESSAGE->folder != $dbox && strpos(self::$MESSAGE->folder, $dbox.$delim) !== 0)
+ if (!empty(self::$MESSAGE->context)
+ || (
+ !empty(self::$MESSAGE->folder)
+ && (self::$MESSAGE->folder != $dbox && strpos(self::$MESSAGE->folder, $dbox.$delim) !== 0)
+ )
) {
return '';
}
@@ -392,7 +395,7 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
$attrib['onerror'] = "this.onerror = null; this.src = '$placeholder';";
}
- if (self::$MESSAGE->sender) {
+ if (!empty(self::$MESSAGE->sender)) {
$photo_img = $rcmail->url([
'_task' => 'addressbook',
'_action' => 'photo',
@@ -642,7 +645,10 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
*/
public static function message_body($attrib)
{
- if (!is_array(self::$MESSAGE->parts) && empty(self::$MESSAGE->body)) {
+ if (
+ empty(self::$MESSAGE)
+ || (!is_array(self::$MESSAGE->parts) && empty(self::$MESSAGE->body))
+ ) {
return '';
}
diff --git a/program/actions/mail/viewsource.php b/program/actions/mail/viewsource.php
index 40c6f7857..46c3d5f87 100644
--- a/program/actions/mail/viewsource.php
+++ b/program/actions/mail/viewsource.php
@@ -45,7 +45,7 @@ class rcmail_action_mail_viewsource extends rcmail_action
$headers = $rcmail->storage->get_message_headers($uid);
}
- $charset = $headers->charset ?: $rcmail->config->get('default_charset');
+ $charset = $headers->charset ?: $rcmail->config->get('default_charset', RCUBE_CHARSET);
if (!empty($_GET['_save'])) {
$subject = rcube_mime::decode_header($headers->subject, $headers->charset);
@@ -59,6 +59,9 @@ class rcmail_action_mail_viewsource extends rcmail_action
]);
}
else {
+ // Make sure it works in an iframe (#9084)
+ $rcmail->output->page_headers();
+
header("Content-Type: text/plain; charset={$charset}");
}
diff --git a/program/actions/settings/folder_edit.php b/program/actions/settings/folder_edit.php
index 2caebc361..134ccc02e 100644
--- a/program/actions/settings/folder_edit.php
+++ b/program/actions/settings/folder_edit.php
@@ -84,7 +84,7 @@ class rcmail_action_settings_folder_edit extends rcmail_action_settings_folders
if (strlen($path)) {
$path_id = $path;
$path = $storage->mod_folder($path . $delimiter);
- if ($path[strlen($path)-1] == $delimiter) {
+ if (($path[strlen($path)-1] ?? '') == $delimiter) {
$path = substr($path, 0, -1);
}
}
diff --git a/program/include/iniset.php b/program/include/iniset.php
index 177895346..c4ab39fc6 100644
--- a/program/include/iniset.php
+++ b/program/include/iniset.php
@@ -24,7 +24,7 @@ if (PHP_VERSION_ID < 70300) {
}
// application constants
-define('RCMAIL_VERSION', '1.6.1');
+define('RCMAIL_VERSION', '1.6-git');
define('RCMAIL_START', microtime(true));
if (!defined('INSTALL_PATH')) {
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index ac3fde321..af186630f 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1043,7 +1043,7 @@ class rcmail extends rcube
// Trash subfolders
$delimiter = $storage->get_hierarchy_delimiter();
- $subfolders = array_reverse($storage->list_folders('', $trash_mbox . $delimiter . '*'));
+ $subfolders = array_reverse($storage->list_folders($trash_mbox . $delimiter, '*'));
$last = '';
foreach ($subfolders as $folder) {
@@ -1180,7 +1180,8 @@ class rcmail extends rcube
return rtrim($path, '/') . '/';
}
- $path = preg_replace('/[?&].*$/', '', (string) $path);
+ $path = preg_replace('/index\.php.*$/', '', (string) $path);
+ $path = preg_replace('/[?&].*$/', '', $path);
$path = preg_replace('![^/]+$!', '', $path);
return rtrim($path, '/') . '/';
diff --git a/program/include/rcmail_action.php b/program/include/rcmail_action.php
index 5d211fb10..9cb41da04 100644
--- a/program/include/rcmail_action.php
+++ b/program/include/rcmail_action.php
@@ -229,15 +229,15 @@ abstract class rcmail_action
$table->add(['colspan' => 3, 'class' => 'root'], rcube::Q($root));
}
- if ($storage = $data['storage']) {
- $percent = min(100, round(($storage['used']/max(1,$storage['total']))*100));
+ if ($storage = ($data['storage'] ?? null)) {
+ $percent = min(100, round(($storage['used'] / max(1, $storage['total'])) * 100));
$table->add('name', rcube::Q($rcmail->gettext('quotastorage')));
$table->add(null, self::show_bytes($storage['total'] * 1024));
$table->add(null, sprintf('%s (%.0f%%)', self::show_bytes($storage['used'] * 1024), $percent));
}
- if ($message = $data['message']) {
- $percent = min(100, round(($message['used']/max(1,$message['total']))*100));
+ if ($message = ($data['message'] ?? null)) {
+ $percent = min(100, round(($message['used'] / max(1, $message['total'])) * 100));
$table->add('name', rcube::Q($rcmail->gettext('quotamessage')));
$table->add(null, intval($message['total']));
diff --git a/program/include/rcmail_install.php b/program/include/rcmail_install.php
index dc4287726..e8278f8c8 100644
--- a/program/include/rcmail_install.php
+++ b/program/include/rcmail_install.php
@@ -464,10 +464,10 @@ class rcmail_install
else {
$this->config[$replacement] = $current[$prop];
}
- }
- unset($current[$prop]);
- unset($current[$replacement]);
+ unset($current[$prop]);
+ unset($current[$replacement]);
+ }
}
// Merge old *_port options into the new *_host options, where possible
@@ -489,9 +489,9 @@ class rcmail_install
}
// add all ldap_public sources having global_search enabled to autocomplete_addressbooks
- if (is_array($current['ldap_public'])) {
+ if (!empty($current['ldap_public']) && is_array($current['ldap_public'])) {
foreach ($current['ldap_public'] as $key => $ldap_public) {
- if ($ldap_public['global_search']) {
+ if (!empty($ldap_public['global_search'])) {
$this->config['autocomplete_addressbooks'][] = $key;
unset($current['ldap_public'][$key]['global_search']);
}
@@ -499,10 +499,6 @@ class rcmail_install
}
$this->config = array_merge($this->config, $current);
-
- foreach (array_keys((array) $current['ldap_public']) as $key) {
- $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
- }
}
/**
diff --git a/program/include/rcmail_oauth.php b/program/include/rcmail_oauth.php
index 938a274bc..fc592eeb6 100644
--- a/program/include/rcmail_oauth.php
+++ b/program/include/rcmail_oauth.php
@@ -17,6 +17,9 @@
+-----------------------------------------------------------------------+
*/
+if (stream_resolve_include_path('GuzzleHttp/autoload.php'))
+ include_once 'GuzzleHttp/autoload.php';
+
use GuzzleHttp\Client;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Exception\RequestException;
@@ -120,8 +123,12 @@ class rcmail_oauth
*/
public function get_redirect_uri()
{
+ $url = $this->rcmail->url([], true, true);
+
// rewrite redirect URL to not contain query parameters because some providers do not support this
- return preg_replace('/\/?\?_task=[a-z]+/', '/index.php/login/oauth', $this->rcmail->url([], true, true));
+ $url = preg_replace('/\?.*/', '', $url);
+
+ return slashify($url) . 'index.php/login/oauth';
}
/**
@@ -142,7 +149,7 @@ class rcmail_oauth
*/
public function jwt_decode($jwt)
{
- list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt);
+ list($headb64, $bodyb64, $cryptob64) = explode('.', strtr($jwt, '-_', '+/'));
$header = json_decode(base64_decode($headb64), true);
$body = json_decode(base64_decode($bodyb64), true);
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
index 1408ac61a..862ad014a 100644
--- a/program/include/rcmail_output_html.php
+++ b/program/include/rcmail_output_html.php
@@ -1581,10 +1581,10 @@ EOF;
foreach ($this->$source as $name => $vars) {
// $vars can be in many forms:
// - string
- // - array('key' => 'val')
- // - array(string, string)
- // - array(array(), string)
- // - array(array('key' => 'val'), array('key' => 'val'))
+ // - ['key' => 'val']
+ // - [string, string]
+ // - [[], string]
+ // - [['key' => 'val'], ['key' => 'val']]
// normalise this for processing by checking for string array keys
$vars = is_array($vars) ? (count(array_filter(array_keys($vars), 'is_string')) > 0 ? [$vars] : $vars) : [$vars];
@@ -2772,7 +2772,7 @@ EOF;
}
}
}
- else {
+ else if ($type != 'link') {
$template_logo = $logo;
}
}
diff --git a/program/include/rcmail_sendmail.php b/program/include/rcmail_sendmail.php
index 9e5c3d5f6..6b3ecfbf3 100644
--- a/program/include/rcmail_sendmail.php
+++ b/program/include/rcmail_sendmail.php
@@ -1058,6 +1058,10 @@ class rcmail_sendmail
$charset = !empty($message->headers) ? $message->headers->charset : RCUBE_CHARSET;
$separator = ', ';
+ if (!isset($this->data['recipients'])) {
+ $this->data['recipients'] = [];
+ }
+
// we have a set of recipients stored is session
if (
$header == 'to'
@@ -1085,7 +1089,7 @@ class rcmail_sendmail
if ($header == 'to') {
$mailfollowup = $message->headers->others['mail-followup-to'] ?? [];
$mailreplyto = $message->headers->others['mail-reply-to'] ?? [];
- $reply_all = $message->reply_all ?? null;
+ $reply_all = $this->data['reply_all'] ?? null;
// Reply to mailing list...
if ($reply_all == 'list' && $mailfollowup) {
@@ -1125,7 +1129,7 @@ class rcmail_sendmail
}
}
// add recipient of original message if reply to all
- else if ($header == 'cc' && !empty($message->reply_all) && $message->reply_all != 'list') {
+ else if ($header == 'cc' && !empty($this->data['reply_all']) && $this->data['reply_all'] != 'list') {
if ($v = $message->headers->to) {
$fvalue .= $v;
}
@@ -1176,7 +1180,6 @@ class rcmail_sendmail
$from_email = !empty($this->data['ident']['email']) ? mb_strtolower($this->data['ident']['email']) : '';
$to_addresses = rcube_mime::decode_address_list($fvalue, null, $decode_header, $charset);
$fvalue = [];
- $recipients = [];
foreach ($to_addresses as $addr_part) {
if (empty($addr_part['mailto'])) {
@@ -1190,14 +1193,14 @@ class rcmail_sendmail
if (
($header == 'to' || $mode != self::MODE_REPLY || $mailto_lc != $from_email)
- && !in_array($mailto_lc, $recipients)
+ && !in_array($mailto_lc, $this->data['recipients'])
) {
if ($addr_part['name'] && $mailto != $addr_part['name']) {
$mailto = format_email_recipient($mailto, $addr_part['name']);
}
- $fvalue[] = $mailto;
- $recipients[] = $mailto_lc;
+ $fvalue[] = $mailto;
+ $this->data['recipients'][] = $mailto_lc;
}
}
diff --git a/program/include/rcmail_utils.php b/program/include/rcmail_utils.php
index 9036cbcfb..14626d993 100644
--- a/program/include/rcmail_utils.php
+++ b/program/include/rcmail_utils.php
@@ -170,7 +170,7 @@ class rcmail_utils
$dir .= '/' . $db->db_provider;
if (!file_exists($dir)) {
- if ($opts['errors']) {
+ if (!empty($opts['errors'])) {
rcube::raise_error("DDL Upgrade files for " . $db->db_provider . " driver not found.", false, true);
}
return false;
@@ -253,7 +253,7 @@ class rcmail_utils
*
* @param string $package Package name
*
- * @return string Version string
+ * @return null|string Version string
*/
public static function db_version($package = 'roundcube')
{
@@ -265,6 +265,9 @@ class rcmail_utils
$package . '-version');
$row = $db->fetch_array();
+ if ($row === false) {
+ return null;
+ }
$version = preg_replace('/[^0-9]/', '', $row[0]);
return $version;
diff --git a/program/js/app.js b/program/js/app.js
index f37089d57..68135eeec 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -2647,11 +2647,13 @@ function rcube_webmail()
if (!rc.env.frame_lock)
rc.env.frame_lock = rc.set_busy(true, 'loading');
- if (target.frameElement)
- $(target.frameElement).on('load.lock', function(e) {
- rc.unlock_frame();
- $(this).off('load.lock');
- });
+ try {
+ if (target.frameElement)
+ $(target.frameElement).on('load.lock', function(e) {
+ rc.unlock_frame();
+ $(this).off('load.lock');
+ });
+ } catch(e) { /* Ignore permission denied error */ };
};
this.unlock_frame = function()
@@ -4242,7 +4244,7 @@ function rcube_webmail()
var container = $(this.gui_objects.editform).find('.identity-encryption').first();
var identity_email = $(this.gui_objects.editform).find('.ff_email').val().trim();
- if (!container.length || !identity_email || !this.mailvelope_keyring.createKeyGenContainer)
+ if (!container.length || !identity_email || !this.mailvelope_keyring.createKeyGenContainer)
return;
var key_fingerprint;
diff --git a/program/js/common.js b/program/js/common.js
index f6b4829d3..5384d84b5 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -429,11 +429,10 @@ function rcube_check_email(input, inline, count, strict)
ipv6 = '\\[IPv6:[0-9a-f:.]+\\]',
ip_addr = '(' + ipv4 + ')|(' + ipv6 + ')',
// Use simplified domain matching, because we need to allow Unicode characters here
- // So, e-mail address should be validated also on server side after idn_to_ascii() use
- //domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d',
- //sub_domain = '('+atom+'|'+domain_literal+')',
- // allow punycode/unicode top-level domain, allow extended domains (#5588)
- domain = '(('+ip_addr+')|(([^@\\x2e]+\\x2e)+([^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,})))',
+ // So, e-mail address should be validated also on server side after idn_to_ascii()
+ // Allow punycode/unicode top-level domains, allow extended domains (#5588)
+ // Allow a domain ending with .s (#8854)
+ domain = '(('+ip_addr+')|(([^@.]+\\.)+([^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|s|xn--[a-z0-9]{2,})))',
// ICANN e-mail test (http://idn.icann.org/E-mail_test)
icann_domains = [
'\\u0645\\u062b\\u0627\\u0644\\x2e\\u0625\\u062e\\u062a\\u0628\\u0627\\u0631',
diff --git a/program/js/list.js b/program/js/list.js
index 3ab77f014..9aae32b9f 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -643,8 +643,8 @@ drag_row: function(e, id)
if (!this.is_event_target(e))
return true;
- // accept right-clicks
- if (rcube_event.get_button(e) == 2)
+ // handle only left-clicks
+ if (rcube_event.get_button(e) != 0)
return true;
this.in_selection_before = e && e.istouch || this.in_selection(id) ? id : false;
diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php
index b871a0e00..e8be95b64 100644
--- a/program/lib/Roundcube/bootstrap.php
+++ b/program/lib/Roundcube/bootstrap.php
@@ -58,7 +58,7 @@ foreach ($config as $optname => $optval) {
}
// framework constants
-define('RCUBE_VERSION', '1.6.1');
+define('RCUBE_VERSION', '1.6-git');
define('RCUBE_CHARSET', 'UTF-8');
define('RCUBE_TEMP_FILE_PREFIX', 'RCMTEMP');
@@ -100,8 +100,9 @@ if (!preg_match($regexp, $path)) {
spl_autoload_register('rcube_autoload');
// set PEAR error handling (will also load the PEAR main class)
-PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, function($err) { rcube::raise_error($err, true); });
-
+if (class_exists('PEAR')) {
+ PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, function($err) { rcube::raise_error($err, true); });
+}
/**
* Similar function as in_array() but case-insensitive with multibyte support.
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index 00e39ea7b..bf7dc4206 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -543,7 +543,7 @@ class html_checkbox extends html_inputfield
}
// set 'checked' attribute
- $this->attrib['checked'] = (string) $value === ((string) $this->attrib['value'] ?? '');
+ $this->attrib['checked'] = (string) $value === (string) ($this->attrib['value'] ?? '');
return parent::show();
}
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 1f7552605..57e4d0fa2 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -1396,7 +1396,7 @@ class rcube
'message' => $arg->getMessage(),
];
}
- else if ($arg instanceof PEAR_Error) {
+ else if (is_object($arg) && is_a($arg, 'PEAR_Error')) {
$info = $arg->getUserInfo();
$arg = [
'code' => $arg->getCode(),
diff --git a/program/lib/Roundcube/rcube_charset.php b/program/lib/Roundcube/rcube_charset.php
index 94e539612..ce5f81418 100644
--- a/program/lib/Roundcube/rcube_charset.php
+++ b/program/lib/Roundcube/rcube_charset.php
@@ -66,6 +66,7 @@ class rcube_charset
'222' => 'WINDOWS-874',
'238' => 'WINDOWS-1250',
'MS950' => 'CP950',
+ 'WINDOWS31J' => 'CP932',
'WINDOWS949' => 'UHC',
'WINDOWS1257' => 'ISO-8859-13',
'ISO2022JP' => 'ISO-2022-JP-MS',
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index 4b64482bd..9c0b5d9fa 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -341,6 +341,16 @@ class rcube_db
}
}
+ /**
+ * Getter for an information about the last error.
+ *
+ * @return ?array [SQLSTATE error code, driver specific error code, driver specific error message]
+ */
+ public function error_info()
+ {
+ return $this->dbh ? $this->dbh->errorInfo() : null;
+ }
+
/**
* Getter for error state
*
diff --git a/program/lib/Roundcube/rcube_html2text.php b/program/lib/Roundcube/rcube_html2text.php
index edc35468a..7b964a985 100644
--- a/program/lib/Roundcube/rcube_html2text.php
+++ b/program/lib/Roundcube/rcube_html2text.php
@@ -143,9 +143,10 @@ class rcube_html2text
*/
protected $search = [
'/\r/', // Non-legal carriage return
- '/<head[^>]*>.*?<\/head>/is', // <head>
- '/<script[^>]*>.*?<\/script>/is', // <script>
- '/<style[^>]*>.*?<\/style>/is', // <style>
+ '/\n*<\/?html>\n*/is', // <html>
+ '/\n*<head[^>]*>.*?<\/head>\n*/is', // <head>
+ '/\n*<script[^>]*>.*?<\/script>\n*/is', // <script>
+ '/\n*<style[^>]*>.*?<\/style>\n*/is', // <style>
'/[\n\t]+/', // Newlines and tabs
'/<p[^>]*>/i', // <p>
'/<\/p>[\s\n\t]*<div[^>]*>/i', // </p> before <div>
@@ -172,6 +173,7 @@ class rcube_html2text
*/
protected $replace = [
'', // Non-legal carriage return
+ '', // <html>|</html>
'', // <head>
'', // <script>
'', // <style>
@@ -502,6 +504,7 @@ class rcube_html2text
if (($pos = stripos($text, '<body')) !== false) {
$pos = strpos($text, '>', $pos);
$text = substr($text, $pos + 1);
+ $text = ltrim($text);
}
// Run our defined tags search-and-replace
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index aa4282a04..d5ec7d735 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -1663,7 +1663,7 @@ class rcube_imap extends rcube_storage
$results = $searcher->exec(
$folder,
$search,
- $charset ? $charset : $this->default_charset,
+ $charset ?: $this->default_charset,
$sort_field && $this->get_capability('SORT') ? $sort_field : null,
$this->threading
);
@@ -2060,15 +2060,17 @@ class rcube_imap extends rcube_storage
// find first non-array entry
for ($i=1; $i<count($part); $i++) {
- if (!is_array($part[$i])) {
+ if (is_string($part[$i])) {
$struct->ctype_secondary = strtolower($part[$i]);
// read content type parameters
if (isset($part[$i+1]) && is_array($part[$i+1])) {
$struct->ctype_parameters = [];
for ($j=0; $j<count($part[$i+1]); $j+=2) {
- $param = strtolower($part[$i+1][$j]);
- $struct->ctype_parameters[$param] = $part[$i+1][$j+1];
+ if (is_string($part[$i+1][$j])) {
+ $param = strtolower($part[$i+1][$j]);
+ $struct->ctype_parameters[$param] = $part[$i+1][$j+1];
+ }
}
}
@@ -2146,7 +2148,9 @@ class rcube_imap extends rcube_storage
if (is_array($part[$params_idx])) {
$struct->ctype_parameters = [];
for ($i=0; $i<count($part[$params_idx]); $i+=2) {
- $struct->ctype_parameters[strtolower($part[$params_idx][$i])] = $part[$params_idx][$i+1];
+ if (is_string($part[$params_idx][$i])) {
+ $struct->ctype_parameters[strtolower($part[$params_idx][$i])] = $part[$params_idx][$i+1];
+ }
}
if (isset($struct->ctype_parameters['charset'])) {
@@ -2187,7 +2191,9 @@ class rcube_imap extends rcube_storage
}
if (is_array($part[$di][1])) {
for ($n=0; $n<count($part[$di][1]); $n+=2) {
- $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
+ if (is_string($part[$di][1][$n])) {
+ $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
+ }
}
}
}
@@ -2442,11 +2448,11 @@ class rcube_imap extends rcube_storage
$part_data = rcube_imap_generic::getStructurePartData($structure, $part);
$o_part = new rcube_message_part;
- $o_part->ctype_primary = $part_data['type'];
- $o_part->ctype_secondary = $part_data['subtype'];
- $o_part->encoding = $part_data['encoding'];
- $o_part->charset = $part_data['charset'];
- $o_part->size = $part_data['size'];
+ $o_part->ctype_primary = $part_data['type'] ?? null;
+ $o_part->ctype_secondary = $part_data['subtype'] ?? null;
+ $o_part->encoding = $part_data['encoding'] ?? null;
+ $o_part->charset = $part_data['charset'] ?? null;
+ $o_part->size = $part_data['size'] ?? 0;
}
$body = '';
@@ -3374,7 +3380,7 @@ class rcube_imap extends rcube_storage
// get list of subscribed folders
if ((strpos($folder, '%') === false) && (strpos($folder, '*') === false)) {
- $a_subscribed = $this->list_folders_subscribed('', $folder . $delm . '*');
+ $a_subscribed = $this->list_folders_subscribed($folder . $delm, '*');
$subscribed = $this->folder_exists($folder, true);
}
else {
@@ -3429,7 +3435,7 @@ class rcube_imap extends rcube_storage
// get list of sub-folders or all folders
// if folder name contains special characters
$path = strpos($folder, '*') === false && strpos($folder, '%') === false ? ($folder . $delm) : '';
- $sub_mboxes = $this->list_folders('', $path . '*');
+ $sub_mboxes = $this->list_folders($path, '*');
// According to RFC3501 deleting a \Noselect folder
// with subfolders may fail. To workaround this we delete
@@ -4515,7 +4521,10 @@ class rcube_imap extends rcube_storage
$path1 = explode($this->delimiter, $str1);
$path2 = explode($this->delimiter, $str2);
- foreach ($path1 as $idx => $folder1) {
+ $len = max(count($path1), count($path2));
+
+ for ($idx = 0; $idx < $len; $idx++) {
+ $folder1 = $path1[$idx] ?? '';
$folder2 = $path2[$idx] ?? '';
if ($folder1 === $folder2) {
diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php
index f465f8262..83d4ad0ad 100644
--- a/program/lib/Roundcube/rcube_imap_generic.php
+++ b/program/lib/Roundcube/rcube_imap_generic.php
@@ -1054,7 +1054,8 @@ class rcube_imap_generic
}
if (!empty($this->prefs['socket_options'])) {
- $context = stream_context_create($this->prefs['socket_options']);
+ $options = array_intersect_key($this->prefs['socket_options'], ['ssl' => 1]);
+ $context = stream_context_create($options);
$this->fp = stream_socket_client($host . ':' . $port, $errno, $errstr,
$this->prefs['timeout'], STREAM_CLIENT_CONNECT, $context);
}
@@ -2768,7 +2769,7 @@ class rcube_imap_generic
$sort_order = $order == 'ASC' ? SORT_ASC : SORT_DESC;
$sort_flags = SORT_STRING | SORT_FLAG_CASE;
- if (in_array($field, ['arrival', 'date', 'internaldate', 'timestamp'])) {
+ if (in_array($field, ['arrival', 'date', 'internaldate', 'timestamp', 'size', 'uid', 'id'])) {
$sort_flags = SORT_NUMERIC;
}
@@ -2869,11 +2870,11 @@ class rcube_imap_generic
$mode = 3;
break;
default:
- $mode = 0;
+ $mode = $formatted ? 4 : 0;
}
// Use BINARY extension when possible (and safe)
- $binary = $binary && $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY');
+ $binary = $binary && $mode && preg_match('/^[0-9.]+$/', (string) $part) && $this->hasCapability('BINARY');
$fetch_mode = $binary ? 'BINARY' : 'BODY';
$partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : '';
@@ -2920,7 +2921,8 @@ class rcube_imap_generic
if ($line[0] == '(' && substr($line, -1) == ')') {
// tokenize content inside brackets
// the content can be e.g.: (UID 9844 BODY[2.4] NIL)
- $tokens = $this->tokenizeResponse(preg_replace('/(^\(|\)$)/', '', $line));
+ $line = preg_replace('/(^\(|\)$)/', '', $line);
+ $tokens = $this->tokenizeResponse($line);
for ($i=0; $i<count($tokens); $i+=2) {
if (preg_match('/^(BODY|BINARY)/i', $tokens[$i])) {
@@ -2930,16 +2932,16 @@ class rcube_imap_generic
}
}
+ // Cyrus IMAP does not return a NO-response on error, but we can detect it
+ // and fallback to a non-binary fetch (#9097)
+ if ($binary && !$found) {
+ $binary = $initiated = false;
+ $line = trim($this->readLine(1024)); // the OK response line
+ continue;
+ }
+
if ($result !== false) {
- if ($mode == 1) {
- $result = base64_decode($result);
- }
- else if ($mode == 2) {
- $result = quoted_printable_decode($result);
- }
- else if ($mode == 3) {
- $result = convert_uudecode($result);
- }
+ $result = $this->decodeContent($result, $mode, true);
}
}
// response with string literal
@@ -2953,65 +2955,37 @@ class rcube_imap_generic
if (!$bytes) {
$result = '';
}
+ // An optimal path for a case when we need the body as-is in a string
+ else if (!$mode && !$file && !$print) {
+ $result = $this->readBytes($bytes);
+ }
else while ($bytes > 0) {
- $line = $this->readBytes($bytes > $chunkSize ? $chunkSize : $bytes);
+ $chunk = $this->readBytes($bytes > $chunkSize ? $chunkSize : $bytes);
- if ($line === '') {
+ if ($chunk === '') {
break;
}
- $len = strlen($line);
+ $len = strlen($chunk);
if ($len > $bytes) {
- $line = substr($line, 0, $bytes);
- $len = strlen($line);
+ $chunk = substr($chunk, 0, $bytes);
+ $len = strlen($chunk);
}
$bytes -= $len;
- // BASE64
- if ($mode == 1) {
- $line = preg_replace('|[^a-zA-Z0-9+=/]|', '', $line);
- // create chunks with proper length for base64 decoding
- $line = $prev.$line;
- $length = strlen($line);
- if ($length % 4) {
- $length = floor($length / 4) * 4;
- $prev = substr($line, $length);
- $line = substr($line, 0, $length);
- }
- else {
- $prev = '';
- }
- $line = base64_decode($line);
- }
- // QUOTED-PRINTABLE
- else if ($mode == 2) {
- $line = rtrim($line, "\t\r\0\x0B");
- $line = quoted_printable_decode($line);
- }
- // UUENCODE
- else if ($mode == 3) {
- $line = rtrim($line, "\t\r\n\0\x0B");
- if ($line == 'end' || preg_match('/^begin\s+[0-7]+\s+.+$/', $line)) {
- continue;
- }
- $line = convert_uudecode($line);
- }
- // default
- else if ($formatted) {
- $line = rtrim($line, "\t\r\n\0\x0B") . "\n";
- }
+ $chunk = $this->decodeContent($chunk, $mode, $bytes <= 0, $prev);
if ($file) {
- if (fwrite($file, $line) === false) {
+ if (fwrite($file, $chunk) === false) {
break;
}
}
else if ($print) {
- echo $line;
+ echo $chunk;
}
else {
- $result .= $line;
+ $result .= $chunk;
}
}
}
@@ -3033,6 +3007,105 @@ class rcube_imap_generic
return false;
}
+ /**
+ * Decodes a chunk of a message part content from a FETCH response.
+ *
+ * @param string $chunk Content
+ * @param int $mode Encoding mode
+ * @param bool $is_last Whether it is a last chunk of data
+ * @param string $prev Extra content from the previous chunk
+ *
+ * @return string Encoded string
+ */
+ protected static function decodeContent($chunk, $mode, $is_last = false, &$prev = '')
+ {
+ // BASE64
+ if ($mode == 1) {
+ $chunk = $prev . preg_replace('|[^a-zA-Z0-9+=/]|', '', $chunk);
+
+ // create chunks with proper length for base64 decoding
+ $length = strlen($chunk);
+
+ if ($length % 4) {
+ $length = floor($length / 4) * 4;
+ $prev = substr($chunk, $length);
+ $chunk = substr($chunk, 0, $length);
+ }
+ else {
+ $prev = '';
+ }
+
+ return base64_decode($chunk);
+ }
+
+ // QUOTED-PRINTABLE
+ if ($mode == 2) {
+ if (!self::decodeContentChunk($chunk, $prev, $is_last)) {
+ return '';
+ }
+
+ $chunk = preg_replace('/[\t\r\0\x0B]+\n/', "\n", $chunk);
+
+ return quoted_printable_decode($chunk);
+ }
+
+ // X-UUENCODE
+ if ($mode == 3) {
+ if (!self::decodeContentChunk($chunk, $prev, $is_last)) {
+ return '';
+ }
+
+ $chunk = preg_replace(
+ ['/\r?\n/', '/(^|\n)end$/', '/^begin\s+[0-7]{3,4}\s+[^\n]+\n/'],
+ ["\n", '', ''],
+ $chunk
+ );
+
+ if (!strlen($chunk)) {
+ return '';
+ }
+
+ return convert_uudecode($chunk);
+ }
+
+ // Plain text formatted
+ // TODO: Formatting should be handled outside of this class
+ if ($mode == 4) {
+ if (!self::decodeContentChunk($chunk, $prev, $is_last)) {
+ return '';
+ }
+
+ if ($is_last) {
+ $chunk = rtrim($chunk, "\t\r\n\0\x0B");
+ }
+
+ return preg_replace('/[\t\r\0\x0B]+\n/', "\n", $chunk);
+ }
+
+ return $chunk;
+ }
+
+ /**
+ * A helper for a new-line aware parsing. See self::decodeContent().
+ */
+ private static function decodeContentChunk(&$chunk, &$prev, $is_last)
+ {
+ $chunk = $prev . $chunk;
+ $prev = '';
+
+ if (!$is_last) {
+ if (($pos = strrpos($chunk, "\n")) !== false) {
+ $prev = substr($chunk, $pos + 1);
+ $chunk = substr($chunk, 0, $pos + 1);
+ } else {
+ $prev = $chunk;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Handler for IMAP APPEND command
*
diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php
index 69308d603..cee84f8b3 100644
--- a/program/lib/Roundcube/rcube_ldap.php
+++ b/program/lib/Roundcube/rcube_ldap.php
@@ -84,6 +84,11 @@ class rcube_ldap extends rcube_addressbook
$fetch_attributes = ['objectClass'];
+ // Disable VLV by default
+ if (!isset($this->prop['vlv'])) {
+ $this->prop['vlv'] = false;
+ }
+
// check if groups are configured
if (!empty($p['groups']) && is_array($p['groups'])) {
$this->groups = true;
@@ -187,7 +192,7 @@ class rcube_ldap extends rcube_addressbook
(array) ($this->coltypes['address']['subtypes'] ?? null),
(array) ($this->coltypes['locality']['subtypes'] ?? null)
),
- ] + (array) $this->coltypes['address'];
+ ] + (array) ($this->coltypes['address'] ?? []);
foreach (['street','locality','zipcode','region','country'] as $childcol) {
if (!empty($this->coltypes[$childcol])) {
@@ -221,19 +226,15 @@ class rcube_ldap extends rcube_addressbook
}
}
- // make sure 'required_fields' is an array
- if (!isset($this->prop['required_fields'])) {
- $this->prop['required_fields'] = [];
- }
- else if (!is_array($this->prop['required_fields'])) {
- $this->prop['required_fields'] = (array) $this->prop['required_fields'];
- }
+ // make sure 'required_fields' and 'autovalues' are an array
+ $this->prop['required_fields'] = (array) ($this->prop['required_fields'] ?? []);
+ $this->prop['autovalues'] = (array) ($this->prop['autovalues'] ?? []);
// make sure LDAP_rdn field is required
if (
!empty($this->prop['LDAP_rdn'])
&& !in_array($this->prop['LDAP_rdn'], $this->prop['required_fields'])
- && !in_array($this->prop['LDAP_rdn'], array_keys((array)$this->prop['autovalues']))
+ && !array_key_exists($this->prop['LDAP_rdn'], $this->prop['autovalues'])
) {
$this->prop['required_fields'][] = $this->prop['LDAP_rdn'];
}
@@ -328,9 +329,9 @@ class rcube_ldap extends rcube_addressbook
$conf = $rcube->plugins->exec_hook('ldap_connected', $this->prop + ['host' => $host]);
$bind_pass = $conf['bind_pass'] ?? null;
- $bind_user = $conf['bind_user'] ?? null;
- $bind_dn = $conf['bind_dn'] ?? null;
- $auth_method = $conf['auth_method'] ?? null;
+ $bind_user = $conf['bind_user'] ?? '';
+ $bind_dn = $conf['bind_dn'] ?? '';
+ $auth_method = $conf['auth_method'] ?? '';
$this->base_dn = $this->groups_base_dn = $conf['base_dn'] ?? null;
if (!empty($conf['groups']['base_dn'])) {
@@ -803,7 +804,7 @@ class rcube_ldap extends rcube_addressbook
*/
function _entry_sort_cmp($a, $b)
{
- return strcmp($a[$this->sort_col][0], $b[$this->sort_col][0]);
+ return strcmp($a[$this->sort_col][0] ?? '', $b[$this->sort_col][0] ?? '');
}
/**
@@ -860,11 +861,13 @@ class rcube_ldap extends rcube_addressbook
foreach ($ldap_data as $entry) {
$rec = $this->_ldap2result($entry);
foreach ($fields as $f) {
- foreach ((array)$rec[$f] as $val) {
- if ($this->compare_search_value($f, $val, $search, $mode)) {
- $this->result->add($rec);
- $this->result->count++;
- break 2;
+ if (!empty($rec[$f])) {
+ foreach ((array)$rec[$f] as $val) {
+ if ($this->compare_search_value($f, $val, $search, $mode)) {
+ $this->result->add($rec);
+ $this->result->count++;
+ break 2;
+ }
}
}
}
@@ -908,18 +911,19 @@ class rcube_ldap extends rcube_addressbook
$filter .= ')';
}
else {
+ $attributes = [];
+
if ($fields == '*') {
- // search_fields are required for fulltext search
- if (empty($this->prop['search_fields'])) {
- $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch');
- $this->result = new rcube_result_set();
- return $this->result;
+ $attributes = (array) ($this->prop['search_fields'] ?? []);
+
+ // If search fields aren't configured use some common fields
+ if (empty($search_fields)) {
+ $fields = ['name', 'surname', 'firstname', 'email'];
}
- $attributes = (array) $this->prop['search_fields'];
}
- else {
- // map address book fields into ldap attributes
- $attributes = [];
+
+ // map address book fields into ldap attributes
+ if (empty($attributes)) {
foreach ((array) $fields as $field) {
if (!empty($this->coltypes[$field]) && !empty($this->coltypes[$field]['attributes'])) {
$attributes = array_merge($attributes, (array) $this->coltypes[$field]['attributes']);
@@ -1026,11 +1030,14 @@ class rcube_ldap extends rcube_addressbook
// add general filter to query
if (!empty($this->prop['filter'])) {
- $prop['filter'] = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['filter']) . ')' . $prop['filter'] . ')';
+ $prop['filter'] = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['filter']) . ')' . $filter . ')';
}
}
- $result = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], $attrs, $prop, $count);
+ $search_scope = $prop['scope'] ?? 'sub';
+ $search_filter = $prop['filter'] ?? '(objectclass=*)';
+
+ $result = $this->ldap->search($base_dn, $search_filter, $search_scope, $attrs, $prop, $count);
$result_count = 0;
// we have a search result resource, get all entries
@@ -1046,18 +1053,19 @@ class rcube_ldap extends rcube_addressbook
&& is_array($this->prop['group_filters'])
&& !empty($this->prop['groups']['filter'])
) {
- $filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['groups']['filter']) . ')' . $filter . ')';
-
- // for groups we may use cn instead of displayname...
- if ($this->prop['fieldmap']['name'] != $this->prop['groups']['name_attr']) {
- $filter = str_replace(strtolower($this->prop['fieldmap']['name']) . '=', $this->prop['groups']['name_attr'] . '=', $filter);
- }
-
$name_attr = $this->prop['groups']['name_attr'];
$email_attr = $this->prop['groups']['email_attr'] ?: 'mail';
$attrs = array_unique(['dn', 'objectClass', $name_attr, $email_attr]);
- $res = $this->ldap->search($this->groups_base_dn, $filter, $this->prop['groups']['scope'], $attrs, $prop, $count);
+ $search_scope = $this->prop['groups']['scope'] ?? 'sub';
+ $search_filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['groups']['filter']) . ')' . $filter . ')';
+
+ // for groups we may use cn instead of displayname...
+ if ($this->prop['fieldmap']['name'] != $name_attr) {
+ $search_filter = str_replace(strtolower($this->prop['fieldmap']['name']) . '=', $name_attr . '=', $search_filter);
+ }
+
+ $res = $this->ldap->search($this->groups_base_dn, $search_filter, $search_scope, $attrs, $prop, $count);
if ($count && $res) {
$result += $res;
@@ -1571,7 +1579,7 @@ class rcube_ldap extends rcube_addressbook
$attrvals['{'.$k.'}'] = is_array($v) ? $v[0] : $v;
}
- foreach ((array) $this->prop['autovalues'] as $lf => $templ) {
+ foreach ($this->prop['autovalues'] as $lf => $templ) {
if (empty($attrs[$lf])) {
if (strpos($templ, '(') !== false) {
// replace {attr} placeholders with (escaped!) attribute values to be safely eval'd
@@ -1617,9 +1625,15 @@ class rcube_ldap extends rcube_addressbook
// determine record type
if ($this->is_group_entry($rec)) {
- $out['_type'] = 'group';
- $out['readonly'] = true;
- $fieldmap['name'] = $this->group_data['name_attr'] ?: $this->prop['groups']['name_attr'];
+ $out['_type'] = 'group';
+ $out['readonly'] = true;
+
+ if (!empty($this->group_data['name_attr'])) {
+ $fieldmap['name'] = $this->group_data['name_attr'];
+ }
+ else if (!empty($this->prop['groups']['name_attr'])) {
+ $fieldmap['name'] = $this->prop['groups']['name_attr'];
+ }
}
// assign object type from object class mapping
@@ -1795,6 +1809,10 @@ class rcube_ldap extends rcube_addressbook
*/
private function is_group_entry($entry)
{
+ if (empty($entry['objectclass'])) {
+ return false;
+ }
+
$classes = array_map('strtolower', (array)$entry['objectclass']);
return count(array_intersect(array_keys($this->group_types), $classes)) > 0;
@@ -1900,10 +1918,10 @@ class rcube_ldap extends rcube_addressbook
$base_dn = $this->groups_base_dn;
$filter = $this->prop['groups']['filter'];
- $scope = $this->prop['groups']['scope'];
- $name_attr = $this->prop['groups']['name_attr'];
- $email_attr = $this->prop['groups']['email_attr'] ?: 'mail';
- $sort_attrs = (array) ($this->prop['groups']['sort'] ? $this->prop['groups']['sort'] : $name_attr);
+ $scope = $this->prop['groups']['scope'] ?? 'sub';
+ $name_attr = !empty($this->prop['groups']['name_attr']) ? $this->prop['groups']['name_attr'] : 'cn';
+ $email_attr = !empty($this->prop['groups']['email_attr']) ? $this->prop['groups']['email_attr'] : 'mail';
+ $sort_attrs = (array) (!empty($this->prop['groups']['sort']) ? $this->prop['groups']['sort'] : $name_attr);
$sort_attr = $sort_attrs[0];
$page_size = 200;
@@ -1920,7 +1938,7 @@ class rcube_ldap extends rcube_addressbook
$ldap->set_vlv_page($vlv_page+1, $page_size);
}
- $props = ['sort' => $this->prop['groups']['sort']];
+ $props = ['sort' => $this->prop['groups']['sort'] ?? null];
$attrs = array_unique(['dn', 'objectClass', $name_attr, $email_attr, $sort_attr]);
// add search filter
@@ -2015,11 +2033,12 @@ class rcube_ldap extends rcube_addressbook
if ($list = $this->ldap->read_entries($dn, '(objectClass=*)', $attrs)) {
$entry = $list[0];
$group_name = is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
+ $classes = !empty($entry['objectclass']) ? $entry['objectclass'] : [];
$group_cache[$group_id]['ID'] = $group_id;
$group_cache[$group_id]['dn'] = $dn;
$group_cache[$group_id]['name'] = $group_name;
- $group_cache[$group_id]['member_attr'] = $this->get_group_member_attr($entry['objectclass']);
+ $group_cache[$group_id]['member_attr'] = $this->get_group_member_attr($classes);
}
else {
$group_cache[$group_id] = false;
diff --git a/program/lib/Roundcube/rcube_message_header.php b/program/lib/Roundcube/rcube_message_header.php
index 07c8a2346..0326eb8a0 100644
--- a/program/lib/Roundcube/rcube_message_header.php
+++ b/program/lib/Roundcube/rcube_message_header.php
@@ -68,6 +68,13 @@ class rcube_message_header
*/
public $cc;
+ /**
+ * Message hidden recipients (Bcc)
+ *
+ * @var string
+ */
+ public $bcc;
+
/**
* Message Reply-To header
*
@@ -201,6 +208,21 @@ class rcube_message_header
*/
public $flags = [];
+ /**
+ * Extra flags (for the messages list)
+ *
+ * @var array
+ * @deprecated Use $flags
+ */
+ public $list_flags = [];
+
+ /**
+ * Extra columns content (for the messages list)
+ *
+ * @var array
+ */
+ public $list_cols = [];
+
/**
* Message structure
*
diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php
index cce4e6105..001f8683f 100644
--- a/program/lib/Roundcube/rcube_plugin_api.php
+++ b/program/lib/Roundcube/rcube_plugin_api.php
@@ -341,18 +341,20 @@ class rcube_plugin_api
if (is_readable($composer) && ($json = json_decode(file_get_contents($composer), true))) {
// Build list of plugins required
$require = [];
- foreach (array_keys((array) $json['require']) as $dname) {
- if (!preg_match('|^([^/]+)/([a-zA-Z0-9_-]+)$|', $dname, $m)) {
- continue;
- }
+ if (!empty($json['require'])) {
+ foreach (array_keys((array) $json['require']) as $dname) {
+ if (!preg_match('|^([^/]+)/([a-zA-Z0-9_-]+)$|', $dname, $m)) {
+ continue;
+ }
- $vendor = $m[1];
- $name = $m[2];
+ $vendor = $m[1];
+ $name = $m[2];
- if ($name != 'plugin-installer' && $vendor != 'pear' && $vendor != 'pear-pear') {
- $dpath = unslashify($dir->path) . "/$name/$name.php";
- if (is_readable($dpath)) {
- $require[] = $name;
+ if ($name != 'plugin-installer' && $vendor != 'pear' && $vendor != 'pear-pear') {
+ $dpath = unslashify($dir->path) . "/$name/$name.php";
+ if (is_readable($dpath)) {
+ $require[] = $name;
+ }
}
}
}
diff --git a/program/lib/Roundcube/rcube_string_replacer.php b/program/lib/Roundcube/rcube_string_replacer.php
index b1b7fcb9e..55795e003 100644
--- a/program/lib/Roundcube/rcube_string_replacer.php
+++ b/program/lib/Roundcube/rcube_string_replacer.php
@@ -59,8 +59,8 @@ class rcube_string_replacer
$link_prefix = "([\w]+:\/\/|{$this->noword}[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)";
$this->options = $options;
- $this->linkref_index = '/\[([^\]#]+)\](:?\s*' . substr($this->pattern, 1, -1) . ')/';
- $this->linkref_pattern = '/\[([^\]#]+)\]/';
+ $this->linkref_index = '/\[([^<>\]#]+)\](:?\s*' . substr($this->pattern, 1, -1) . ')/';
+ $this->linkref_pattern = '/\[([^<>\]#]+)\]/';
$this->link_pattern = "/$link_prefix($utf_domain([$url1]*[$url2]+)*)/";
$this->mailto_pattern = "/("
. "[-\w!\#\$%&*+~\/^`|{}=]+(?:\.[-\w!\#\$%&*+~\/^`|{}=]+)*" // local-part
@@ -152,7 +152,7 @@ class rcube_string_replacer
$matches[0][1]
];
- return $this->get_replacement($this->add('['.$key.']')) . $matches[2][0];
+ return $this->get_replacement($this->add('[' . $key . ']')) . $matches[2][0];
}
/**
diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
index a641497d5..2724c015e 100644
--- a/program/lib/Roundcube/rcube_utils.php
+++ b/program/lib/Roundcube/rcube_utils.php
@@ -748,8 +748,8 @@ class rcube_utils
*/
public static function parse_host_uri($host, $plain_port = null, $ssl_port = null)
{
- if (strpos($host, 'unix://') === 0) {
- return [$host, 'unix', -1];
+ if (preg_match('#^(unix|ldapi)://#i', $host, $matches)) {
+ return [$host, $matches[1], -1];
}
$url = parse_url($host);
@@ -1509,12 +1509,17 @@ class rcube_utils
}
if (strpos($format, 'u') !== false) {
- $dt = number_format(microtime(true), 6, '.', '');
- $dt .= '.' . date_default_timezone_get();
+ $dt = number_format(microtime(true), 6, '.', '');
+
+ try {
+ $date = date_create_from_format('U.u', $dt);
+ $date->setTimeZone(new DateTimeZone(date_default_timezone_get()));
- if ($date = date_create_from_format('U.u.e', $dt)) {
return $date->format($format);
}
+ catch (Exception $e) {
+ // ignore, fallback to date()
+ }
}
return date($format);
diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php
index 5fbfb4ed6..4601649c1 100644
--- a/program/lib/Roundcube/rcube_vcard.php
+++ b/program/lib/Roundcube/rcube_vcard.php
@@ -201,7 +201,7 @@ class rcube_vcard
$key = $col;
$subtype = '';
- if (!empty($raw['type'])) {
+ if (!empty($raw['type']) && is_array($raw['type'])) {
$raw['type'] = array_map('strtolower', $raw['type']);
$combined = implode(',', array_diff($raw['type'], ['internet', 'pref']));
@@ -877,7 +877,17 @@ class rcube_vcard
}
else {
foreach ((array)$attrvalues as $attrvalue) {
- $attr .= strtoupper(";$attrname=") . self::vcard_quote($attrvalue, ',');
+ $attrname = strtoupper($attrname);
+ // TYPE=OTHER is non-standard, TYPE=INTERNET is redundant, remove these
+ if ($attrname == 'TYPE') {
+ $attrvalue = array_map('strtolower', (array) $attrvalue);
+ $attrvalue = array_diff($attrvalue, ['other', 'internet']);
+ if (empty($attrvalue)) {
+ continue;
+ }
+ }
+
+ $attr .= ';' . $attrname . '=' . self::vcard_quote($attrvalue, ',');
}
}
}
diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php
index 78db69e0c..14464fcd4 100644
--- a/program/lib/Roundcube/rcube_washtml.php
+++ b/program/lib/Roundcube/rcube_washtml.php
@@ -744,7 +744,7 @@ class rcube_washtml
// space(s) between <NOBR>
'/(<\/nobr>)(\s+)(<nobr>)/i',
// PHP bug #32547 workaround: remove title tag
- '/<title[^>]*>.*<\/title>/i',
+ '/<title[^>]*>.*<\/title>/iU',
// remove <!doctype> before BOM (#1490291)
'/<\!doctype[^>]+>[^<]*/im',
// byte-order mark (only outlook?)
diff --git a/skins/elastic/ui.js b/skins/elastic/ui.js
index 75343e1be..5f5ce0d89 100644
--- a/skins/elastic/ui.js
+++ b/skins/elastic/ui.js
@@ -870,8 +870,8 @@ function rcube_elastic_ui()
$('.popup', context).addClass('formcontent').append(
$('<div class="form-group row">')
- .append(label.attr('for', id).addClass('col-sm-2 col-form-label'))
- .append($('<div class="col-sm-10">').append(input))
+ .append(label.attr('for', id).addClass('col-sm-4 col-form-label text-break'))
+ .append($('<div class="col-sm-8">').append(input))
);
input.focus();
Attachment:
signature.asc
Description: PGP signature