git: f770ff0c0dcc - main - mail/cyrus-imapd36: update to 3.6.5
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 05 Jun 2024 12:22:02 UTC
The branch main has been updated by ume:
URL: https://cgit.FreeBSD.org/ports/commit/?id=f770ff0c0dcc788d58a44176ff990068d96523aa
commit f770ff0c0dcc788d58a44176ff990068d96523aa
Author: Hajimu UMEMOTO <ume@FreeBSD.org>
AuthorDate: 2024-06-05 12:16:12 +0000
Commit: Hajimu UMEMOTO <ume@FreeBSD.org>
CommitDate: 2024-06-05 12:21:54 +0000
mail/cyrus-imapd36: update to 3.6.5
Relnotes: https://www.cyrusimap.org/3.6/imap/download/release-notes/3.6/x/3.6.5.html
Security: CVE-2024-34055
---
mail/cyrus-imapd36/Makefile | 6 +-
mail/cyrus-imapd36/distinfo | 6 +-
mail/cyrus-imapd36/files/v36-CVE-2024-34055.patch | 5348 ---------------------
3 files changed, 5 insertions(+), 5355 deletions(-)
diff --git a/mail/cyrus-imapd36/Makefile b/mail/cyrus-imapd36/Makefile
index 8fed513b9d5a..5dca3baa72bc 100644
--- a/mail/cyrus-imapd36/Makefile
+++ b/mail/cyrus-imapd36/Makefile
@@ -1,6 +1,6 @@
PORTNAME= cyrus-imapd
-PORTVERSION= 3.6.4
-PORTREVISION= 1
+PORTVERSION= 3.6.5
+PORTREVISION= 0
CATEGORIES= mail
MASTER_SITES= https://github.com/cyrusimap/cyrus-imapd/releases/download/${PORTNAME}-${DISTVERSION}/
PKGNAMESUFFIX= ${CYRUS_IMAPD_VER}
@@ -20,8 +20,6 @@ http_PKGNAMESUFFIX= ${CYRUS_IMAPD_VER}-http
CYRUS_IMAPD_VER= 36
-EXTRA_PATCHES= ${FILESDIR}/v36-CVE-2024-34055.patch:-p1
-
LIB_DEPENDS= libsasl2.so:security/cyrus-sasl2 \
libicuuc.so:devel/icu \
libjansson.so:devel/jansson \
diff --git a/mail/cyrus-imapd36/distinfo b/mail/cyrus-imapd36/distinfo
index 4028c0592aef..d44a6e6a8767 100644
--- a/mail/cyrus-imapd36/distinfo
+++ b/mail/cyrus-imapd36/distinfo
@@ -1,3 +1,3 @@
-TIMESTAMP = 1710505927
-SHA256 (cyrus-imapd-3.6.4.tar.gz) = 4fa6ee42a08d842b9aab812fdace1bb4966e63f1ad9718345338e5896eb723e2
-SIZE (cyrus-imapd-3.6.4.tar.gz) = 13866715
+TIMESTAMP = 1717583649
+SHA256 (cyrus-imapd-3.6.5.tar.gz) = c807d6c566a8ff0986a5162eaa3077d57bb78e313d826e27140b5def3f7ac0b1
+SIZE (cyrus-imapd-3.6.5.tar.gz) = 13897004
diff --git a/mail/cyrus-imapd36/files/v36-CVE-2024-34055.patch b/mail/cyrus-imapd36/files/v36-CVE-2024-34055.patch
deleted file mode 100644
index 8761b618599e..000000000000
--- a/mail/cyrus-imapd36/files/v36-CVE-2024-34055.patch
+++ /dev/null
@@ -1,5348 +0,0 @@
-From d95b0b211e8179b62fb3a814c827db5175968d01 Mon Sep 17 00:00:00 2001
-From: Robert Stepanek <rsto@fastmailteam.com>
-Date: Wed, 3 Jan 2024 09:51:36 +0100
-Subject: [PATCH 01/16] SearchFuzzy.pm: do not use non-standard XSNIPPETS
- command
-
-The XSNIPPETS and XCONVMULTISTANDARD commands in Cyrus got
-deprecated, so don't keep our test using it.
-
-Signed-off-by: Robert Stepanek <rsto@fastmailteam.com>
----
- cassandane/Cassandane/Cyrus/SearchFuzzy.pm | 344 +++++++++------------
- 1 file changed, 146 insertions(+), 198 deletions(-)
-
-diff --git a/cassandane/Cassandane/Cyrus/SearchFuzzy.pm b/cassandane/Cassandane/Cyrus/SearchFuzzy.pm
-index af0adaf64..5b3aad0e0 100644
---- a/cassandane/Cassandane/Cyrus/SearchFuzzy.pm
-+++ b/cassandane/Cassandane/Cyrus/SearchFuzzy.pm
-@@ -43,6 +43,8 @@ use warnings;
- use Cwd qw(abs_path);
- use DateTime;
- use Data::Dumper;
-+use MIME::Base64 qw(encode_base64);
-+use Encode qw(decode encode);
-
- use lib '.';
- use base qw(Cassandane::Cyrus::TestCase);
-@@ -50,10 +52,19 @@ use Cassandane::Util::Log;
-
- sub new
- {
-+
- my ($class, @args) = @_;
- my $config = Cassandane::Config->default()->clone();
-- $config->set(conversations => 'on');
-- return $class->SUPER::new({ config => $config }, @args);
-+ $config->set(
-+ conversations => 'on',
-+ httpallowcompress => 'no',
-+ httpmodules => 'jmap',
-+ );
-+ return $class->SUPER::new({
-+ config => $config,
-+ jmap => 1,
-+ services => [ 'imap', 'http' ]
-+ }, @args);
- }
-
- sub set_up
-@@ -134,6 +145,55 @@ sub create_testmessages
- $self->{instance}->run_command({cyrus => 1}, 'squatter');
- }
-
-+sub get_snippets
-+{
-+ # Previous versions of this test module used XSNIPPETS to
-+ # assert snippets but this command got removed from Cyrus.
-+ # Use JMAP instead.
-+
-+ my ($self, $folder, $uids, $filter) = @_;
-+
-+ my $imap = $self->{store}->get_client();
-+ my $jmap = $self->{jmap};
-+
-+ $self->assert_not_null($jmap);
-+
-+ $imap->select($folder);
-+ my $res = $imap->fetch($uids, ['emailid']);
-+ my %emailIdToImapUid = map { $res->{$_}{emailid}[0] => $_ } keys %$res;
-+
-+ $res = $jmap->CallMethods([
-+ ['SearchSnippet/get', {
-+ filter => $filter,
-+ emailIds => [ keys %emailIdToImapUid ],
-+ }, 'R1'],
-+ ]);
-+
-+ my @snippets;
-+ foreach (@{$res->[0][1]{list}}) {
-+ if ($_->{subject}) {
-+ push(@snippets, [
-+ 0,
-+ $emailIdToImapUid{$_->{emailId}},
-+ 'SUBJECT',
-+ $_->{subject},
-+ ]);
-+ }
-+ if ($_->{preview}) {
-+ push(@snippets, [
-+ 0,
-+ $emailIdToImapUid{$_->{emailId}},
-+ 'BODY',
-+ $_->{preview},
-+ ]);
-+ }
-+ }
-+
-+ return {
-+ snippets => [ sort { $a->[1] <=> $b->[1] } @snippets ],
-+ };
-+}
-+
- sub test_copy_messages
- :needs_search_xapian
- {
-@@ -151,12 +211,13 @@ sub test_copy_messages
- }
-
- sub test_stem_verbs
-- :min_version_3_0 :needs_search_xapian
-+ :min_version_3_0 :needs_search_xapian :JMAPExtensions
- {
- my ($self) = @_;
- $self->create_testmessages();
-
- my $talk = $self->{store}->get_client();
-+ $self->assert_not_null($self->{jmap});
-
- xlog $self, "Select INBOX";
- my $r = $talk->select("INBOX") || die;
-@@ -175,11 +236,8 @@ sub test_stem_verbs
- $r = $talk->search('fuzzy', ['subject', { Quote => "runs" }]) || die;
- $self->assert_num_equals(3, scalar @$r);
-
-- xlog $self, 'XSNIPPETS for FUZZY subject "runs"';
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'subject', { Quote => 'runs' }]
-- ) || die;
-+ xlog $self, 'Get snippets for FUZZY subject "runs"';
-+ $r = $self->get_snippets('INBOX', $uids, { subject => 'runs' });
- $self->assert_num_equals(3, scalar @{$r->{snippets}});
- }
-
-@@ -250,12 +308,8 @@ sub test_snippet_wildcard
- $talk->select("INBOX") || die;
- my $uidvalidity = $talk->get_response_code('uidvalidity');
-
-- xlog $self, "XSNIPPETS for $term";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => "$term*" }]
-- ) || die;
-- xlog $self, Dumper($r);
-+ xlog $self, "Get snippets for $term";
-+ $r = $self->get_snippets('INBOX', $uids, { 'text' => "$term*" });
- $self->assert_num_equals(2, scalar @{$r->{snippets}});
- }
-
-@@ -358,13 +412,17 @@ sub test_normalize_snippets
- my ($self) = @_;
-
- # Set up test message with funny characters
-- my $body = "foo gären советской diĝir naïve léger";
-- my @terms = split / /, $body;
-+use utf8;
-+ my @terms = ( "gären", "советской", "diĝir", "naïve", "léger" );
-+no utf8;
-+ my $body = encode_base64(encode('UTF-8', join(' ', @terms)));
-+ $body =~ s/\r?\n/\r\n/gs;
-
- xlog $self, "Generate and index test messages.";
- my %params = (
- mime_charset => "utf-8",
-- body => $body
-+ mime_encoding => 'base64',
-+ body => $body,
- );
- $self->make_message("1", %params) || die;
-
-@@ -380,24 +438,20 @@ sub test_normalize_snippets
-
- # Assert that diacritics are matched and returned
- foreach my $term (@terms) {
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-- $self->assert_num_not_equals(index($r->{snippets}[0][3], "<b>$term</b>"), -1);
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
-+ $self->assert_num_not_equals(index($r->{snippets}[0][3], "<mark>$term</mark>"), -1);
- }
-
- # Assert that search without diacritics matches
- if ($self->{skipdiacrit}) {
- my $term = "naive";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-- $self->assert_num_not_equals(index($r->{snippets}[0][3], "<b>naïve</b>"), -1);
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { 'text' => $term });
-+use utf8;
-+ $self->assert_num_not_equals(index($r->{snippets}[0][3], "<mark>naïve</mark>"), -1);
-+no utf8;
- }
-+
- }
-
- sub test_skipdiacrit
-@@ -499,38 +553,23 @@ sub test_snippets_termcover
- my $r = $talk->select("INBOX") || die;
- my $uidvalidity = $talk->get_response_code('uidvalidity');
- my $uids = $talk->search('1:*', 'NOT', 'DELETED');
-- my $want = "<b>favourite</b> <b>cereal</b>";
-+ my $want = "<mark>favourite</mark> <mark>cereal</mark>";
-
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [
-- 'fuzzy', 'text', 'favourite',
-- 'fuzzy', 'text', 'cereal',
-- 'fuzzy', 'text', { Quote => 'bogus gnarly' }
-- ]
-- ) || die;
-+ $r = $self->get_snippets('INBOX', $uids, {
-+ operator => 'AND',
-+ conditions => [{
-+ text => 'favourite',
-+ }, {
-+ text => 'cereal',
-+ }, {
-+ text => '"bogus gnarly"'
-+ }],
-+ });
- $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], $want));
-
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [
-- 'fuzzy', 'text', 'favourite cereal'
-- ]
-- ) || die;
-- $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], $want));
--
-- # Regression - a phrase is treated as a loose term
-- $r = $talk->xsnippets( [ [ 'INBOX', $uidvalidity, $uids ] ],
-- 'utf-8', [
-- 'fuzzy', 'text', { Quote => 'favourite nope cereal' },
-- 'fuzzy', 'text', { Quote => 'bogus gnarly' }
-- ]
-- ) || die;
-- $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], $want));
--
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [
-- 'fuzzy', 'text', { Quote => 'favourite cereal' }
-- ]
-- ) || die;
-+ $r = $self->get_snippets('INBOX', $uids, {
-+ text => 'favourite cereal',
-+ });
- $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], $want));
- }
-
-@@ -542,18 +581,28 @@ sub test_cjk_words
-
- xlog $self, "Generate and index test messages.";
-
-+use utf8;
- my $body = "明末時已經有香港地方的概念";
-+no utf8;
-+ $body = encode_base64(encode('UTF-8', $body));
-+ $body =~ s/\r?\n/\r\n/gs;
- my %params = (
- mime_charset => "utf-8",
-- body => $body
-+ mime_encoding => 'base64',
-+ body => $body,
- );
- $self->make_message("1", %params) || die;
-
- # Splits into the words: "み, 円, 月額, 申込
-+use utf8;
- $body = "申込み!月額円";
-+no utf8;
-+ $body = encode_base64(encode('UTF-8', $body));
-+ $body =~ s/\r?\n/\r\n/gs;
- %params = (
- mime_charset => "utf-8",
-- body => $body
-+ mime_encoding => 'base64',
-+ body => $body,
- );
- $self->make_message("2", %params) || die;
-
-@@ -569,50 +618,45 @@ sub test_cjk_words
-
- my $term;
- # Search for a two-character CJK word
-+use utf8;
- $term = "已經";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-- $self->assert_num_not_equals(index($r->{snippets}[0][3], "<b>$term</b>"), -1);
-+no utf8;
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
-+ $self->assert_num_not_equals(index($r->{snippets}[0][3], "<mark>$term</mark>"), -1);
-
- # Search for the CJK words 明末 and 時, note that the
- # word order is reversed to the original message
-+use utf8;
- $term = "時明末";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-+no utf8;
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
- $self->assert_num_equals(scalar @{$r->{snippets}}, 1);
-
- # Search for the partial CJK word 月
-+use utf8;
- $term = "月";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-+no utf8;
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
- $self->assert_num_equals(scalar @{$r->{snippets}}, 0);
-
- # Search for the interleaved, partial CJK word 額申
-+use utf8;
- $term = "額申";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-+no utf8;
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
- $self->assert_num_equals(scalar @{$r->{snippets}}, 0);
-
- # Search for three of four words: "み, 月額, 申込",
- # in different order than the original.
-+use utf8;
- $term = "月額み申込";
-- xlog $self, "XSNIPPETS for FUZZY text \"$term\"";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'text', { Quote => $term }]
-- ) || die;
-+no utf8;
-+ xlog $self, "Get snippets for FUZZY text \"$term\"";
-+ $r = $self->get_snippets('INBOX', $uids, { text => $term });
- $self->assert_num_equals(scalar @{$r->{snippets}}, 1);
- }
-
-@@ -805,86 +849,6 @@ sub test_xattachmentname
- }
-
-
--sub test_xapianv2
-- :min_version_3_0 :needs_search_xapian
--{
-- my ($self) = @_;
--
-- my $talk = $self->{store}->get_client();
--
-- # This is a smallish regression test to check if we break something
-- # obvious by moving Xapian indexing from folder:uid to message guids.
-- #
-- # Apart from the tests in this module, at least also the following
-- # imodules are relevant: Metadata for SORT, Thread for THREAD.
--
-- xlog $self, "Generate message";
-- my $r = $self->make_message("I run", body => "Run, Forrest! Run!" ) || die;
-- my $uid = $r->{attrs}->{uid};
--
-- xlog $self, "Copy message into INBOX";
-- $talk->copy($uid, "INBOX");
--
-- xlog $self, "Run squatter";
-- $self->{instance}->run_command({cyrus => 1}, 'squatter');
--
-- $r = $talk->xconvmultisort(
-- [ qw(reverse arrival) ],
-- [ 'conversations', position => [1,10] ],
-- 'utf-8', 'fuzzy', 'text', "run",
-- );
-- $self->assert_num_equals(2, scalar @{$r->{sort}[0]} - 1);
-- $self->assert_num_equals(1, scalar @{$r->{sort}});
--
-- xlog $self, "Create target mailbox";
-- $talk->create("INBOX.target");
--
-- xlog $self, "Copy message into INBOX.target";
-- $talk->copy($uid, "INBOX.target");
--
-- xlog $self, "Run squatter";
-- $self->{instance}->run_command({cyrus => 1}, 'squatter');
--
-- $r = $talk->xconvmultisort(
-- [ qw(reverse arrival) ],
-- [ 'conversations', position => [1,10] ],
-- 'utf-8', 'fuzzy', 'text', "run",
-- );
-- $self->assert_num_equals(3, scalar @{$r->{sort}[0]} - 1);
-- $self->assert_num_equals(1, scalar @{$r->{sort}});
--
-- xlog $self, "Generate message";
-- $self->make_message("You run", body => "A running joke" ) || die;
--
-- xlog $self, "Run squatter";
-- $self->{instance}->run_command({cyrus => 1}, 'squatter');
--
-- $r = $talk->xconvmultisort(
-- [ qw(reverse arrival) ],
-- [ 'conversations', position => [1,10] ],
-- 'utf-8', 'fuzzy', 'text', "run",
-- );
-- $self->assert_num_equals(2, scalar @{$r->{sort}});
--
-- xlog $self, "SEARCH FUZZY";
-- $r = $talk->search(
-- "charset", "utf-8", "fuzzy", "text", "run",
-- ) || die;
-- $self->assert_num_equals(3, scalar @$r);
--
-- xlog $self, "Select INBOX";
-- $r = $talk->select("INBOX") || die;
-- my $uidvalidity = $talk->get_response_code('uidvalidity');
-- my $uids = $talk->search('1:*', 'NOT', 'DELETED');
--
-- xlog $self, "XSNIPPETS";
-- $r = $talk->xsnippets(
-- [['INBOX', $uidvalidity, $uids]], 'utf-8',
-- ['fuzzy', 'body', 'run'],
-- ) || die;
-- $self->assert_num_equals(3, scalar @{$r->{snippets}});
--}
--
- sub test_snippets_escapehtml
- :min_version_3_0 :needs_search_xapian
- {
-@@ -914,21 +878,15 @@ sub test_snippets_escapehtml
- my $uids = $talk->search('1:*', 'NOT', 'DELETED');
- my %m;
-
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'text', 'test1' ]
-- ) || die;
--
-+ $r = $self->get_snippets('INBOX', $uids, { 'text' => 'test1' });
- %m = map { lc($_->[2]) => $_->[3] } @{ $r->{snippets} };
-- $self->assert_str_equals("<b>Test1</b> body with the same tag as snippets", $m{body});
-- $self->assert_str_equals("<b>Test1</b> subject with an unescaped & in it", $m{subject});
--
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'text', 'test2' ]
-- ) || die;
-+ $self->assert_str_equals("<mark>Test1</mark> body with the same tag as snippets", $m{body});
-+ $self->assert_str_equals("<mark>Test1</mark> subject with an unescaped & in it", $m{subject});
-
-+ $r = $self->get_snippets('INBOX', $uids, { 'text' => 'test2' });
- %m = map { lc($_->[2]) => $_->[3] } @{ $r->{snippets} };
-- $self->assert_str_equals("<b>Test2</b> body with a <tag/>, although it's plain text", $m{body});
-- $self->assert_str_equals("<b>Test2</b> subject with a <tag> in it", $m{subject});
-+ $self->assert_str_equals("<mark>Test2</mark> body with a <tag/>, although it's plain text", $m{body});
-+ $self->assert_str_equals("<mark>Test2</mark> subject with a <tag> in it", $m{subject});
- }
-
- sub test_search_exactmatch
-@@ -963,13 +921,10 @@ sub test_search_exactmatch
- $self->assert_num_equals(1, scalar @$uids);
-
- my %m;
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'body', $query ]
-- ) || die;
--
-+ $r = $self->get_snippets('INBOX', $uids, { body => $query });
- %m = map { lc($_->[2]) => $_->[3] } @{ $r->{snippets} };
-- $self->assert(index($m{body}, "<b>some text</b>") != -1);
-- $self->assert(index($m{body}, "<b>some</b> long <b>text</b>") == -1);
-+ $self->assert(index($m{body}, "<mark>some text</mark>") != -1);
-+ $self->assert(index($m{body}, "<mark>some</mark> long <mark>text</mark>") == -1);
- }
-
- sub test_search_subjectsnippet
-@@ -1004,10 +959,7 @@ sub test_search_subjectsnippet
- $self->assert_num_equals(1, scalar @$uids);
-
- my %m;
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'text', $query ]
-- ) || die;
--
-+ $r = $self->get_snippets('INBOX', $uids, { text => $query });
- %m = map { lc($_->[2]) => $_->[3] } @{ $r->{snippets} };
- $self->assert_matches(qr/^\[plumbing\]/, $m{subject});
- }
-@@ -1317,11 +1269,10 @@ sub test_detect_language
- $self->assert_deep_equals([1], $uids);
-
- my $r = $talk->select("INBOX") || die;
-- my $uidvalidity = $talk->get_response_code('uidvalidity');
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'body', 'atmet' ]
-- ) || die;
-- $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], ' Höhe <b>atmeten</b>.'));
-+ $r = $self->get_snippets('INBOX', $uids, { body => 'atmet' });
-+use utf8;
-+ $self->assert_num_not_equals(-1, index($r->{snippets}[0][3], ' Höhe <mark>atmeten</mark>.'));
-+no utf8;
- }
-
- sub test_detect_language_subject
-@@ -1377,12 +1328,9 @@ sub test_detect_language_subject
- $self->assert_deep_equals([1], $uids);
-
- my $r = $talk->select("INBOX") || die;
-- my $uidvalidity = $talk->get_response_code('uidvalidity');
-- $r = $talk->xsnippets( [ [ 'inbox', $uidvalidity, $uids ] ],
-- 'utf-8', [ 'fuzzy', 'subject', 'Landschaft' ]
-- ) || die;
-+ $r = $self->get_snippets('INBOX', $uids, { subject => 'Landschaft' });
- $self->assert_str_equals(
-- 'A subject with the German word <b>Landschaften</b>',
-+ 'A subject with the German word <mark>Landschaften</mark>',
- $r->{snippets}[0][3]
- );
- }
---
-2.39.2
-
-
-From cce755f3a49f3768058c4f52b2c32763e890a6b8 Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Wed, 7 Feb 2024 14:00:00 -0500
-Subject: [PATCH 02/16] imapd.c: UIDVALIDITY should be uint32_t and parse it as
- such
-
----
- imap/imapd.c | 10 +++-------
- imap/index.h | 2 +-
- 2 files changed, 4 insertions(+), 8 deletions(-)
-
-diff --git a/imap/imapd.c b/imap/imapd.c
-index 6f70820ca..8e087b731 100644
---- a/imap/imapd.c
-+++ b/imap/imapd.c
-@@ -4279,15 +4279,11 @@ static void cmd_select(char *tag, char *cmd, char *name)
- }
- else if ((client_capa & CAPA_QRESYNC) &&
- !strcmp(arg.s, "QRESYNC")) {
-- char *p;
--
- if (c != ' ') goto badqresync;
- c = prot_getc(imapd_in);
- if (c != '(') goto badqresync;
-- c = getastring(imapd_in, imapd_out, &arg);
-- v->uidvalidity = strtoul(arg.s, &p, 10);
-- if (*p || !v->uidvalidity || v->uidvalidity == ULONG_MAX) goto badqresync;
-- if (c != ' ') goto badqresync;
-+ c = getuint32(imapd_in, &v->uidvalidity);
-+ if (c != ' ' || !v->uidvalidity) goto badqresync;
- c = getmodseq(imapd_in, &v->modseq);
- if (c == EOF) goto badqresync;
- if (c == ' ') {
-@@ -4427,7 +4423,7 @@ static void cmd_select(char *tag, char *cmd, char *name)
- prot_printf(backend_current->out, "%s %s {" SIZE_T_FMT "+}\r\n%s",
- tag, cmd, strlen(name), name);
- if (v->uidvalidity) {
-- prot_printf(backend_current->out, " (QRESYNC (%lu " MODSEQ_FMT,
-+ prot_printf(backend_current->out, " (QRESYNC (%u " MODSEQ_FMT,
- v->uidvalidity, v->modseq);
- if (v->sequence) {
- prot_printf(backend_current->out, " %s", v->sequence);
-diff --git a/imap/index.h b/imap/index.h
-index ce8fe36c2..df12e609a 100644
---- a/imap/index.h
-+++ b/imap/index.h
-@@ -72,7 +72,7 @@ extern unsigned client_capa;
- struct message;
-
- struct vanished_params {
-- unsigned long uidvalidity;
-+ uint32_t uidvalidity;
- modseq_t modseq;
- const char *match_seq;
- const char *match_uid;
---
-2.39.2
-
-
-From f37421f2687b811c4e4bd8c0fc5c66d368cc3ffe Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Fri, 9 Feb 2024 08:13:05 -0500
-Subject: [PATCH 03/16] imapd.c: consolidate ID field-value parse error
- response
-
----
- imap/imapd.c | 17 +++++------------
- 1 file changed, 5 insertions(+), 12 deletions(-)
-
-diff --git a/imap/imapd.c b/imap/imapd.c
-index 8e087b731..8773621e0 100644
---- a/imap/imapd.c
-+++ b/imap/imapd.c
-@@ -3104,19 +3104,12 @@ static void cmd_id(char *tag)
-
- /* get field name */
- c = getstring(imapd_in, imapd_out, &field);
-- if (c != ' ') {
-+ if (c != ' ' ||
-+ /* get field value */
-+ (c = getnstring(imapd_in, imapd_out, &arg)) == EOF ||
-+ (c != ' ' && c != ')')) {
- prot_printf(imapd_out,
-- "%s BAD Invalid/missing field name in Id\r\n",
-- tag);
-- eatline(imapd_in, c);
-- return;
-- }
--
-- /* get field value */
-- c = getnstring(imapd_in, imapd_out, &arg);
-- if (c != ' ' && c != ')') {
-- prot_printf(imapd_out,
-- "%s BAD Invalid/missing value in Id\r\n",
-+ "%s BAD Invalid field-value pair in Id\r\n",
- tag);
- eatline(imapd_in, c);
- return;
---
-2.39.2
-
-
-From 0ed0466636f43ea32cb765c33fa6a8109d9ea69e Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Wed, 7 Feb 2024 14:12:41 -0500
-Subject: [PATCH 04/16] imapd.c: response code in fatal() string MUST
- immediately follow "* BYE"
-
----
- imap/imapd.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/imap/imapd.c b/imap/imapd.c
-index 8773621e0..464d9d7ad 100644
---- a/imap/imapd.c
-+++ b/imap/imapd.c
-@@ -1186,7 +1186,8 @@ EXPORTED void fatal(const char *s, int code)
- }
- recurse_code = code;
- if (imapd_out) {
-- prot_printf(imapd_out, "* BYE Fatal error: %s\r\n", s);
-+ prot_printf(imapd_out, "* BYE %s%s\r\n",
-+ *s == '[' /* resp-text-code */ ? "" : "Fatal error: ", s);
- prot_flush(imapd_out);
- }
- if (stages.count) {
---
-2.39.2
-
-
-From fb0eee5f3c4d67c0ee84d8e33bd835d9f0044e4a Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Fri, 23 Feb 2024 11:00:19 -0500
-Subject: [PATCH 05/16] imapparse.c: include [TOOBIG] response code for
- oversized word/qstring
-
----
- imap/imapparse.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/imap/imapparse.c b/imap/imapparse.c
-index 02cc1ed92..d096584e3 100644
---- a/imap/imapparse.c
-+++ b/imap/imapparse.c
-@@ -74,7 +74,7 @@ EXPORTED int getword(struct protstream *in, struct buf *buf)
- }
- buf_putc(buf, c);
- if (config_maxword && buf_len(buf) > config_maxword) {
-- fatal("word too long", EX_IOERR);
-+ fatal("[TOOBIG] Word too long", EX_IOERR);
- }
- }
- }
-@@ -138,7 +138,7 @@ EXPORTED int getxstring(struct protstream *pin, struct protstream *pout,
- }
- buf_putc(buf, c);
- if (config_maxquoted && buf_len(buf) > config_maxquoted) {
-- fatal("quoted value too long", EX_IOERR);
-+ fatal("[TOOBIG] Quoted value too long", EX_IOERR);
- }
- }
-
-@@ -212,6 +212,9 @@ EXPORTED int getxstring(struct protstream *pin, struct protstream *pout,
- return c;
- }
- buf_putc(buf, c);
-+ if (config_maxword && buf_len(buf) > config_maxword) {
-+ fatal("[TOOBIG] Word too long", EX_IOERR);
-+ }
- c = prot_getc(pin);
- }
- /* never gets here */
---
-2.39.2
-
-
-From dc9846028db0022372b4cfe4d5ea92ab22eb2ed0 Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Fri, 9 Feb 2024 13:31:11 -0500
-Subject: [PATCH 06/16] imapparse.c: fatal() when a client violates LITERAL-
- limit
-
----
- imap/imap_err.et | 3 +++
- imap/imapparse.c | 7 +++++--
- 2 files changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/imap/imap_err.et b/imap/imap_err.et
-index a98ec0e1b..072078f94 100644
---- a/imap/imap_err.et
-+++ b/imap/imap_err.et
-@@ -65,6 +65,9 @@ ec IMAP_QUOTA_EXCEEDED,
- ec IMAP_MESSAGE_TOO_LARGE,
- "Message size exceeds fixed limit"
-
-+ec IMAP_LITERAL_MINUS_TOO_LARGE,
-+ "[TOOBIG] Non-synchronizing literal size exceeds 4K"
-+
- ec IMAP_USERFLAG_EXHAUSTED,
- "Too many user flags in mailbox"
-
-diff --git a/imap/imapparse.c b/imap/imapparse.c
-index d096584e3..7a4794600 100644
---- a/imap/imapparse.c
-+++ b/imap/imapparse.c
-@@ -153,8 +153,11 @@ EXPORTED int getxstring(struct protstream *pin, struct protstream *pout,
- buf_reset(buf);
- c = getint32(pin, &len);
- if (c == '+') {
-- // LITERAL- says maximum size is 4096!
-- if (lminus && len > 4096) return EOF;
-+ /* LITERAL- says maximum size is 4096! */
-+ if (lminus && len > 4096) {
-+ /* Fail per RFC 7888, Section 4, choice 2 */
-+ fatal(error_message(IMAP_LITERAL_MINUS_TOO_LARGE), EX_IOERR);
-+ }
- isnowait++;
- c = prot_getc(pin);
- }
---
-2.39.2
-
-
-From 8414e71a9d1fbc56d03d6fddb096484364e3406c Mon Sep 17 00:00:00 2001
-From: Ken Murchison <murch@fastmail.com>
-Date: Mon, 12 Feb 2024 10:54:03 -0500
-Subject: [PATCH 07/16] Cleanup and document the use of prot_setisclient()
-
-Only IMAP(-like) clients send LITERAL+ syntax
----
- backup/backupd.c | 3 +--
- backup/lcb.c | 1 -
- backup/lcb_read.c | 2 --
- backup/lcb_verify.c | 2 --
- cunit/getxstring.testc | 4 ----
- imap/append.c | 1 -
- imap/backend.c | 2 --
- imap/cyr_dbtool.c | 1 -
- imap/dlist.c | 5 ++++-
- imap/imapd.c | 4 ++++
- imap/imapparse.c | 5 +++--
- imap/message.c | 3 ---
- imap/mupdate.c | 3 +++
- imap/sync_server.c | 3 +--
- imap/sync_support.c | 4 ----
- lib/prot.h | 2 +-
- 16 files changed, 17 insertions(+), 28 deletions(-)
-
-diff --git a/backup/backupd.c b/backup/backupd.c
-index 9f8fa7b15..d42ddbb70 100644
---- a/backup/backupd.c
-+++ b/backup/backupd.c
-@@ -229,9 +229,8 @@ EXPORTED int service_main(int argc __attribute__((unused)),
- backupd_in = prot_new(0, 0);
- backupd_out = prot_new(1, 1);
-
-- /* Force use of LITERAL+ so we don't need two way communications */
-+ /* Allow use of LITERAL+ */
- prot_setisclient(backupd_in, 1);
-- prot_setisclient(backupd_out, 1);
-
- /* Find out name of client host */
- backupd_clienthost = get_clienthost(0, &localip, &remoteip);
-diff --git a/backup/lcb.c b/backup/lcb.c
-index dbba85ca7..3f68b1aaa 100644
---- a/backup/lcb.c
-+++ b/backup/lcb.c
-@@ -606,7 +606,6 @@ EXPORTED int backup_reindex(const char *name,
- fprintf(out, "\nfound chunk at offset " OFF_T_FMT "\n\n", member_offset);
-
- struct protstream *member = prot_readcb(_prot_fill_cb, gzuc);
-- prot_setisclient(member, 1); /* don't sync literals */
-
- // FIXME stricter timestamp sequence checks
- time_t member_start_ts = -1;
-diff --git a/backup/lcb_read.c b/backup/lcb_read.c
-index f597c97c5..f2342d69d 100644
---- a/backup/lcb_read.c
-+++ b/backup/lcb_read.c
-@@ -113,7 +113,6 @@ EXPORTED int backup_read_message_data(struct backup *backup,
- if (r) return r;
-
- struct protstream *ps = prot_readcb(_prot_fill_cb, gzuc);
-- prot_setisclient(ps, 1); /* don't sync literals */
- r = parse_backup_line(ps, NULL, NULL, &dl);
- prot_free(ps);
-
-@@ -203,7 +202,6 @@ EXPORTED int backup_prepare_message_upload(struct backup *backup,
- if (!r) {
- struct protstream *ps = prot_readcb(_prot_fill_cb, gzuc);
- int c;
-- prot_setisclient(ps, 1); /* don't sync literals */
- c = parse_backup_line(ps, NULL, NULL, &dl);
- prot_free(ps);
- ps = NULL;
-diff --git a/backup/lcb_verify.c b/backup/lcb_verify.c
-index fb0477a8b..88e748c51 100644
---- a/backup/lcb_verify.c
-+++ b/backup/lcb_verify.c
-@@ -228,7 +228,6 @@ static int _verify_message_cb(const struct backup_message *message, void *rock)
- if (r) return r;
-
- struct protstream *ps = prot_readcb(_prot_fill_cb, vmrock->gzuc);
-- prot_setisclient(ps, 1); /* don't sync literals */
- r = parse_backup_line(ps, NULL, NULL, &dl);
-
- if (r == EOF) {
-@@ -528,7 +527,6 @@ static int verify_chunk_mailbox_links(struct backup *backup, struct backup_chunk
- goto done;
- }
- struct protstream *ps = prot_readcb(_prot_fill_cb, gzuc);
-- prot_setisclient(ps, 1); /* don't sync literals */
-
- struct buf cmd = BUF_INITIALIZER;
- while (1) {
-diff --git a/cunit/getxstring.testc b/cunit/getxstring.testc
-index f5a5989a3..12efe86aa 100644
---- a/cunit/getxstring.testc
-+++ b/cunit/getxstring.testc
-@@ -72,9 +72,6 @@ static int tear_down(void)
-
- /*
- * Run a single testcase.
-- *
-- * Note: prot_setisclient() turns off off literal synchronising so
-- * we don't have to futz around with testing that.
- */
- #define _TESTCASE_PRE(fut, input, retval, consumed) \
- do { \
-@@ -84,7 +81,6 @@ static int tear_down(void)
- long long _consumed = (consumed); \
- p = prot_readmap(input, sizeof(input)-1); \
- CU_ASSERT_PTR_NOT_NULL_FATAL(p); \
-- prot_setisclient(p, 1); \
- c = fut(p, NULL, &b); \
- CU_ASSERT_EQUAL(c, retval); \
- if (_consumed >= 0) { \
-diff --git a/imap/append.c b/imap/append.c
-index f7cf7e770..b8bc7963a 100644
---- a/imap/append.c
-+++ b/imap/append.c
-@@ -436,7 +436,6 @@ static int callout_receive_reply(const char *callout,
- }
-
- p = prot_new(fd, /*write*/0);
-- prot_setisclient(p, 1);
-
- /* read and parse the reply as a dlist */
- c = dlist_parse(results, /*parsekeys*/0, /*isbackup*/0, p);
-diff --git a/imap/backend.c b/imap/backend.c
-index 36b83b4d9..546f41d25 100644
---- a/imap/backend.c
-+++ b/imap/backend.c
-@@ -955,7 +955,6 @@ EXPORTED struct backend *backend_connect_pipe(int infd, int outfd,
- ret->prot = prot;
-
- /* use literal+ to send literals */
-- prot_setisclient(ret->in, 1);
- prot_setisclient(ret->out, 1);
-
- /* Start TLS if required */
-@@ -1153,7 +1152,6 @@ EXPORTED struct backend *backend_connect(struct backend *ret_backend, const char
- ret->prot = prot;
-
- /* use literal+ to send literals */
-- prot_setisclient(ret->in, 1);
- prot_setisclient(ret->out, 1);
-
- /* Start TLS if required */
-diff --git a/imap/cyr_dbtool.c b/imap/cyr_dbtool.c
-index fec57cede..c6d56ec77 100644
---- a/imap/cyr_dbtool.c
-+++ b/imap/cyr_dbtool.c
-@@ -155,7 +155,6 @@ static void batch_commands(struct db *db)
- int r = 0;
-
- prot_setisclient(in, 1);
-- prot_setisclient(out, 1);
-
- while (1) {
- buf_reset(&cmd);
-diff --git a/imap/dlist.c b/imap/dlist.c
-index a2f876cd4..2b73ad5d0 100644
---- a/imap/dlist.c
-+++ b/imap/dlist.c
-@@ -1225,7 +1225,10 @@ EXPORTED int dlist_parsemap(struct dlist **dlp, int parsekey, int isbackup,
- struct dlist *dl = NULL;
-
- stream = prot_readmap(base, len);
*** 4421 LINES SKIPPED ***