From 9ff4275b406360d6f93815464b64c6857f97b321 Mon Sep 17 00:00:00 2001 From: Ken Murchison Date: Wed, 28 Jun 2023 13:37:35 -0400 Subject: [PATCH] Sieve: add implicit_keep_target action --- cassandane/Cassandane/Cyrus/Sieve.pm | 2 + .../tiny-tests/Sieve/implicit-keep-target-bad | 37 +++++++ .../Sieve/implicit-keep-target-mailboxid | 35 ++++++ .../Sieve/implicit-keep-target-mailboxname | 31 ++++++ .../Sieve/implicit-keep-target-none | 51 +++++++++ .../Sieve/implicit-keep-target-specialuse | 31 ++++++ changes/next/sieve-implicit-keep-target | 20 ++++ lib/imapoptions | 2 +- sieve/bc_emit.c | 1 + sieve/bc_eval.c | 35 ++++-- sieve/bc_parse.c | 41 +++---- sieve/bytecode.h | 11 +- sieve/interp.c | 10 ++ sieve/interp.h | 12 ++- sieve/sieve-lex.l | 5 + sieve/sieve.y | 102 ++++++++++++------ sieve/sieved.c | 61 +++++++---- sieve/tree.c | 19 +++- sieve/tree.h | 11 +- 19 files changed, 424 insertions(+), 93 deletions(-) create mode 100644 cassandane/tiny-tests/Sieve/implicit-keep-target-bad create mode 100644 cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxid create mode 100644 cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxname create mode 100644 cassandane/tiny-tests/Sieve/implicit-keep-target-none create mode 100644 cassandane/tiny-tests/Sieve/implicit-keep-target-specialuse create mode 100644 changes/next/sieve-implicit-keep-target diff --git a/cassandane/Cassandane/Cyrus/Sieve.pm b/cassandane/Cassandane/Cyrus/Sieve.pm index 8f9503a0df..60a31fca89 100644 --- a/cassandane/Cassandane/Cyrus/Sieve.pm +++ b/cassandane/Cassandane/Cyrus/Sieve.pm @@ -7275,4 +7275,6 @@ EOF $self->check_messages({ 1 => $msg }, check_guid => 0); } +use Cassandane::Tiny::Loader 'tiny-tests/Sieve'; + 1; diff --git a/cassandane/tiny-tests/Sieve/implicit-keep-target-bad b/cassandane/tiny-tests/Sieve/implicit-keep-target-bad new file mode 100644 index 0000000000..9b49bd913a --- /dev/null +++ b/cassandane/tiny-tests/Sieve/implicit-keep-target-bad @@ -0,0 +1,37 @@ +#!perl +use Cassandane::Tiny; + +sub test_implicit_keep_target_bad + :min_version_3_9 :needs_component_sieve :NoAltNameSpace +{ + my ($self) = @_; + + my ($res, $errs) = $self->compile_sievec('norequire', <assert_str_equals('failure', $res); + $self->assert_matches(qr/vnd.cyrus.implicit_keep_target extension MUST be enabled/, $errs); + $self->assert_matches(qr/mailboxid extension MUST be enabled/, $errs); + $self->assert_matches(qr/special-use extension MUST be enabled/, $errs); + + ($res, $errs) = $self->compile_sievec('conflict', <assert_str_equals('failure', $res); + $self->assert_matches(qr/tag :specialuse MUST NOT be used with tag :mailboxid/, $errs); + + ($res, $errs) = $self->compile_sievec('badsyntax', <assert_str_equals('failure', $res); + $self->assert_matches(qr/syntax error/, $errs); +} + diff --git a/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxid b/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxid new file mode 100644 index 0000000000..a39feb9ba8 --- /dev/null +++ b/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxid @@ -0,0 +1,35 @@ +#!perl +use Cassandane::Tiny; + +sub test_implicit_keep_target_mailboxid + :min_version_3_9 :needs_component_sieve :NoAltNameSpace +{ + my ($self) = @_; + + my $folder = "INBOX.foo"; + + xlog $self, "Create folder"; + my $imaptalk = $self->{store}->get_client(); + $imaptalk->create($folder) + or die "Cannot create $folder: $@"; + + xlog $self, "Get folder id"; + my $res = $imaptalk->status($folder, ['mailboxid']); + my $folderid = $res->{mailboxid}[0]; + + xlog $self, "Install a script"; + $self->{instance}->install_sieve_script(<{gen}->generate(subject => "Message 1"); + $self->{instance}->deliver($msg); + + xlog $self, "Check that the message made it to $folder"; + $self->{store}->set_folder($folder); + $self->check_messages({ 1 => $msg }, check_guid => 0); +} + diff --git a/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxname b/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxname new file mode 100644 index 0000000000..06f7e7aed3 --- /dev/null +++ b/cassandane/tiny-tests/Sieve/implicit-keep-target-mailboxname @@ -0,0 +1,31 @@ +#!perl +use Cassandane::Tiny; + +sub test_implicit_keep_target_mailboxname + :min_version_3_9 :needs_component_sieve :NoAltNameSpace +{ + my ($self) = @_; + + my $folder = "INBOX.foo"; + + xlog $self, "Create folder"; + my $imaptalk = $self->{store}->get_client(); + $imaptalk->create($folder) + or die "Cannot create $folder: $@"; + + xlog $self, "Install a script"; + $self->{instance}->install_sieve_script(<{gen}->generate(subject => "Message 1"); + $self->{instance}->deliver($msg); + + xlog $self, "Check that the message made it to $folder"; + $self->{store}->set_folder($folder); + $self->check_messages({ 1 => $msg }, check_guid => 0); +} + diff --git a/cassandane/tiny-tests/Sieve/implicit-keep-target-none b/cassandane/tiny-tests/Sieve/implicit-keep-target-none new file mode 100644 index 0000000000..709a4e7a62 --- /dev/null +++ b/cassandane/tiny-tests/Sieve/implicit-keep-target-none @@ -0,0 +1,51 @@ +#!perl +use Cassandane::Tiny; + +sub test_implicit_keep_target_none + :min_version_3_9 :needs_component_sieve :NoAltNameSpace +{ + my ($self) = @_; + + my $folder = "INBOX.foo"; + + xlog $self, "Create folder"; + my $imaptalk = $self->{store}->get_client(); + $imaptalk->create($folder) + or die "Cannot create $folder: $@"; + + xlog $self, "Allow plus address delivery"; + $imaptalk->setacl($folder, 'anyone' => 'p'); + my $imaptalk = $self->{store}->get_client(); + + xlog $self, "Install a script"; + $self->{instance}->install_sieve_script(<{gen}->generate(subject => "Implicit"); + $self->{instance}->deliver($msg); + + xlog $self, "Check that the message made it to INBOX"; + $self->{store}->set_folder('INBOX'); + $self->{store}->set_fetch_attributes(qw(uid flags)); + $msg->set_attribute(uid => 1); + $msg->set_attribute(flags => [ '\\Recent' ]); + $self->check_messages({ 1 => $msg }, check_guid => 0); + + xlog $self, "Deliver a message to plus address"; + $msg = $self->{gen}->generate(subject => "Explicit"); + $self->{instance}->deliver($msg, user => "cassandane+foo"); + + xlog $self, "Check that the message made it to $folder"; + $self->{store}->set_folder($folder); + $msg->set_attribute(uid => 1); + $msg->set_attribute(flags => [ '\\Recent', '\\Flagged']); + $self->check_messages({ 1 => $msg }, check_guid => 0); +} + diff --git a/cassandane/tiny-tests/Sieve/implicit-keep-target-specialuse b/cassandane/tiny-tests/Sieve/implicit-keep-target-specialuse new file mode 100644 index 0000000000..0bd253c07c --- /dev/null +++ b/cassandane/tiny-tests/Sieve/implicit-keep-target-specialuse @@ -0,0 +1,31 @@ +#!perl +use Cassandane::Tiny; + +sub test_implicit_keep_target_specialuse + :min_version_3_9 :needs_component_sieve :NoAltNameSpace +{ + my ($self) = @_; + + my $folder = "INBOX.foo"; + + xlog $self, "Create folder"; + my $imaptalk = $self->{store}->get_client(); + $imaptalk->create($folder, "(use (\\Important))") + or die "Cannot create $folder: $@"; + + xlog $self, "Install a script"; + $self->{instance}->install_sieve_script(<{gen}->generate(subject => "Message 1"); + $self->{instance}->deliver($msg); + + xlog $self, "Check that the message made it to $folder"; + $self->{store}->set_folder($folder); + $self->check_messages({ 1 => $msg }, check_guid => 0); +} + diff --git a/changes/next/sieve-implicit-keep-target b/changes/next/sieve-implicit-keep-target new file mode 100644 index 0000000000..93bad6009a --- /dev/null +++ b/changes/next/sieve-implicit-keep-target @@ -0,0 +1,20 @@ + +Description: + +Adds an 'implicit_keep_target' action to Sieve to change the target mailbox +for an implicit keep. + +Syntax: + +implicit_keep_target [ :specialuse | :mailboxid ] + + +Config changes: + +The bitfield "vnd.cyrus.implicit_keep_target" has been added to +the "sieve_extensions" option to control the availability of this option. + + +Upgrade instructions: + +None. diff --git a/lib/imapoptions b/lib/imapoptions index c0ba6a9864..f687b3e0ae 100644 --- a/lib/imapoptions +++ b/lib/imapoptions @@ -2574,7 +2574,7 @@ product version in the capabilities assumed. */ */ -{ "sieve_extensions", "fileinto reject vacation vacation-seconds notify include envelope environment body relational regex subaddress copy date index imap4flags mailbox mboxmetadata servermetadata variables editheader extlists duplicate ihave fcc special-use redirect-dsn redirect-deliverby mailboxid vnd.cyrus.log vnd.cyrus.jmapquery vnd.cyrus.imip snooze", BITFIELD("fileinto", "reject", "vacation", "vacation-seconds", "notify", "include", "envelope", "environment", "body", "relational", "regex", "subaddress", "copy", "date", "index", "imap4flags=imapflags", "mailbox", "mboxmetadata", "servermetadata", "variables", "editheader", "extlists", "duplicate", "ihave", "fcc", "special-use", "redirect-dsn", "redirect-deliverby", "mailboxid", "vnd.cyrus.log=x-cyrus-log", "vnd.cyrus.jmapquery=x-cyrus-jmapquery", "vnd.cyrus.imip", "snooze=vnd.cyrus.snooze=x-cyrus-snooze"), "3.6.0" } +{ "sieve_extensions", "fileinto reject vacation vacation-seconds notify include envelope environment body relational regex subaddress copy date index imap4flags mailbox mboxmetadata servermetadata variables editheader extlists duplicate ihave fcc special-use redirect-dsn redirect-deliverby mailboxid vnd.cyrus.log vnd.cyrus.jmapquery vnd.cyrus.imip snooze vnd.cyrus.implicit_keep_target", BITFIELD("fileinto", "reject", "vacation", "vacation-seconds", "notify", "include", "envelope", "environment", "body", "relational", "regex", "subaddress", "copy", "date", "index", "imap4flags=imapflags", "mailbox", "mboxmetadata", "servermetadata", "variables", "editheader", "extlists", "duplicate", "ihave", "fcc", "special-use", "redirect-dsn", "redirect-deliverby", "mailboxid", "vnd.cyrus.log=x-cyrus-log", "vnd.cyrus.jmapquery=x-cyrus-jmapquery", "vnd.cyrus.imip", "snooze=vnd.cyrus.snooze=x-cyrus-snooze", "vnd.cyrus.implicit_keep_target"), "UNRELEASED" } /* Space-separated list of Sieve extensions allowed to be used in sieve scripts, enforced at submission by timsieved(8). Any previously installed script will be unaffected by this option and diff --git a/sieve/bc_emit.c b/sieve/bc_emit.c index 440e4a8747..d53bed0a71 100644 --- a/sieve/bc_emit.c +++ b/sieve/bc_emit.c @@ -528,6 +528,7 @@ static int bc_action_emit(int fd, int codep, int stopcodep, case B_RETURN: case B_SNOOZE: case B_PROCESSIMIP: + case B_IKEEP_TARGET: /* Spew the action parameters */ ret = bc_params_emit(fd, &codep, stopcodep, bc); if (ret < 0) return -1; diff --git a/sieve/bc_eval.c b/sieve/bc_eval.c index 0049316463..9844dbb639 100644 --- a/sieve/bc_eval.c +++ b/sieve/bc_eval.c @@ -1794,9 +1794,9 @@ int sieve_eval_bc(sieve_execute_t *exe, int *impl_keep_p, sieve_interp_t *i, case B_FILEINTO_COPY: case B_FILEINTO_ORIG: { - const char *folder = cmd.u.f.folder; - const char *mailboxid = cmd.u.f.mailboxid; - const char *specialuse = cmd.u.f.specialuse; + const char *folder = cmd.u.f.t.folder; + const char *mailboxid = cmd.u.f.t.mailboxid; + const char *specialuse = cmd.u.f.t.specialuse; struct buf *headers = NULL; if (requires & BFE_VARIABLES) { @@ -1833,16 +1833,16 @@ int sieve_eval_bc(sieve_execute_t *exe, int *impl_keep_p, sieve_interp_t *i, case B_SNOOZE_TZID: case B_SNOOZE_ORIG: { - const char *awaken_mbox = cmd.u.sn.f.folder; - const char *awaken_mboxid = cmd.u.sn.f.mailboxid; - const char *awaken_spluse = cmd.u.sn.f.specialuse; + const char *awaken_mbox = cmd.u.sn.f.t.folder; + const char *awaken_mboxid = cmd.u.sn.f.t.mailboxid; + const char *awaken_spluse = cmd.u.sn.f.t.specialuse; const char *tzid = cmd.u.sn.tzid; strarray_t *addflags = NULL; strarray_t *removeflags = NULL; struct buf *headers = NULL; if (!awaken_mboxid && cmd.u.sn.is_mboxid) { - awaken_mboxid = cmd.u.sn.f.folder; + awaken_mboxid = cmd.u.sn.f.t.folder; awaken_mbox = NULL; } @@ -2162,11 +2162,11 @@ int sieve_eval_bc(sieve_execute_t *exe, int *impl_keep_p, sieve_interp_t *i, { int respond; sieve_fileinto_context_t fcc = { - cmd.u.v.fcc.folder, - cmd.u.v.fcc.specialuse, + cmd.u.v.fcc.t.folder, + cmd.u.v.fcc.t.specialuse, NULL, cmd.u.v.fcc.create, - cmd.u.v.fcc.mailboxid, + cmd.u.v.fcc.t.mailboxid, /*headers*/NULL, /*resolved_mailbox*/NULL }; @@ -2541,6 +2541,12 @@ int sieve_eval_bc(sieve_execute_t *exe, int *impl_keep_p, sieve_interp_t *i, } break; + case B_IKEEP_TARGET: + i->ikeep.folder = cmd.u.ikt.folder; + i->ikeep.mailboxid = cmd.u.ikt.mailboxid; + i->ikeep.specialuse = cmd.u.ikt.specialuse; + break; + case B_ERROR: res = SIEVE_RUN_ERROR; *errmsg = cmd.u.str; @@ -2567,7 +2573,14 @@ int sieve_eval_bc(sieve_execute_t *exe, int *impl_keep_p, sieve_interp_t *i, if (i->edited_headers) i->getheadersection(m, &headers); - res = do_keep(i, sc, actions, actionflags, headers); + if (i->ikeep.folder) { + res = do_fileinto(i, sc, actions, i->ikeep.folder, + i->ikeep.specialuse, 1, 0, i->ikeep.mailboxid, + actionflags, headers); + } + else { + res = do_keep(i, sc, actions, actionflags, headers); + } implicit_keep = 0; } diff --git a/sieve/bc_parse.c b/sieve/bc_parse.c index 4e73e2e05c..e58e906baf 100644 --- a/sieve/bc_parse.c +++ b/sieve/bc_parse.c @@ -65,7 +65,7 @@ static const struct args_t cmd_args_table[] = { { offsetof(struct Commandlist, u.str) } }, { B_FILEINTO_ORIG, "s", /* 4 */ - { offsetof(struct Commandlist, u.f.folder) + { offsetof(struct Commandlist, u.f.t.folder) } }, { B_REDIRECT_ORIG, "s", /* 5 */ { offsetof(struct Commandlist, u.r.address) @@ -118,7 +118,7 @@ static const struct args_t cmd_args_table[] = { { B_RETURN, "", { 0 } }, /* 18 */ { B_FILEINTO_COPY, "is", /* 19 */ { offsetof(struct Commandlist, u.f.copy), - offsetof(struct Commandlist, u.f.folder) + offsetof(struct Commandlist, u.f.t.folder) } }, { B_REDIRECT_COPY, "is", /* 20 */ { offsetof(struct Commandlist, u.r.copy), @@ -139,13 +139,13 @@ static const struct args_t cmd_args_table[] = { { B_FILEINTO_FLAGS, "Sis", /* 23 */ { offsetof(struct Commandlist, u.f.flags), offsetof(struct Commandlist, u.f.copy), - offsetof(struct Commandlist, u.f.folder) + offsetof(struct Commandlist, u.f.t.folder) } }, { B_FILEINTO_CREATE, "iSis", /* 24 */ { offsetof(struct Commandlist, u.f.create), offsetof(struct Commandlist, u.f.flags), offsetof(struct Commandlist, u.f.copy), - offsetof(struct Commandlist, u.f.folder) + offsetof(struct Commandlist, u.f.t.folder) } }, { B_SET, "iss", /* 25 */ { offsetof(struct Commandlist, u.s.modifiers), @@ -216,11 +216,11 @@ static const struct args_t cmd_args_table[] = { offsetof(struct Commandlist, u.v.fcc) } }, { B_FILEINTO_SPECIALUSE, "siSis", /* 38 */ - { offsetof(struct Commandlist, u.f.specialuse), + { offsetof(struct Commandlist, u.f.t.specialuse), offsetof(struct Commandlist, u.f.create), offsetof(struct Commandlist, u.f.flags), offsetof(struct Commandlist, u.f.copy), - offsetof(struct Commandlist, u.f.folder) + offsetof(struct Commandlist, u.f.t.folder) } }, { B_REDIRECT, "ssissiis", /* 39 */ { offsetof(struct Commandlist, u.r.bytime), @@ -233,18 +233,18 @@ static const struct args_t cmd_args_table[] = { offsetof(struct Commandlist, u.r.address) } }, { B_FILEINTO, "ssiSis", /* 40 */ - { offsetof(struct Commandlist, u.f.mailboxid), - offsetof(struct Commandlist, u.f.specialuse), + { offsetof(struct Commandlist, u.f.t.mailboxid), + offsetof(struct Commandlist, u.f.t.specialuse), offsetof(struct Commandlist, u.f.create), offsetof(struct Commandlist, u.f.flags), offsetof(struct Commandlist, u.f.copy), - offsetof(struct Commandlist, u.f.folder) + offsetof(struct Commandlist, u.f.t.folder) } }, { B_LOG, "s", /* 41 */ { offsetof(struct Commandlist, u.l.text) } }, { B_SNOOZE_ORIG, "sSSB2U", /* 42 */ - { offsetof(struct Commandlist, u.sn.f.folder), + { offsetof(struct Commandlist, u.sn.f.t.folder), offsetof(struct Commandlist, u.sn.addflags), offsetof(struct Commandlist, u.sn.removeflags), offsetof(struct Commandlist, u.sn.days), SNOOZE_WDAYS_MASK, @@ -253,7 +253,7 @@ static const struct args_t cmd_args_table[] = { } }, { B_SNOOZE_TZID, "ssSSB2U", /* 43 */ { offsetof(struct Commandlist, u.sn.tzid), - offsetof(struct Commandlist, u.sn.f.folder), + offsetof(struct Commandlist, u.sn.f.t.folder), offsetof(struct Commandlist, u.sn.addflags), offsetof(struct Commandlist, u.sn.removeflags), offsetof(struct Commandlist, u.sn.days), SNOOZE_WDAYS_MASK, @@ -261,9 +261,9 @@ static const struct args_t cmd_args_table[] = { offsetof(struct Commandlist, u.sn.times) } }, { B_SNOOZE, "sssiSSisU", /* 44 */ - { offsetof(struct Commandlist, u.sn.f.folder), - offsetof(struct Commandlist, u.sn.f.mailboxid), - offsetof(struct Commandlist, u.sn.f.specialuse), + { offsetof(struct Commandlist, u.sn.f.t.folder), + offsetof(struct Commandlist, u.sn.f.t.mailboxid), + offsetof(struct Commandlist, u.sn.f.t.specialuse), offsetof(struct Commandlist, u.sn.f.create), offsetof(struct Commandlist, u.sn.addflags), offsetof(struct Commandlist, u.sn.removeflags), @@ -289,6 +289,11 @@ static const struct args_t cmd_args_table[] = { offsetof(struct Commandlist, u.imip.outcome_var), offsetof(struct Commandlist, u.imip.errstr_var) } }, + { B_IKEEP_TARGET, "sss", /* 47 */ + { offsetof(struct Commandlist, u.ikt.mailboxid), + offsetof(struct Commandlist, u.ikt.specialuse), + offsetof(struct Commandlist, u.ikt.folder) + } }, }; static const struct args_t test_args_table[] = { @@ -603,14 +608,14 @@ static int bc_args_parse(bytecode_input_t *bc, int pos, const char *fmt, break; } - pos = bc_string_parse(bc, pos, &fcc->folder); - if (fcc->folder) { + pos = bc_string_parse(bc, pos, &fcc->t.folder); + if (fcc->t.folder) { fcc->create = ntohl(bc[pos++].value); pos = bc_stringlist_parse(bc, pos, &fcc->flags); if (have_specialuse) { - pos = bc_string_parse(bc, pos, &fcc->specialuse); + pos = bc_string_parse(bc, pos, &fcc->t.specialuse); if (have_mailboxid) { - pos = bc_string_parse(bc, pos, &fcc->mailboxid); + pos = bc_string_parse(bc, pos, &fcc->t.mailboxid); } } } diff --git a/sieve/bytecode.h b/sieve/bytecode.h index 582ce9dc5d..bd20afb482 100644 --- a/sieve/bytecode.h +++ b/sieve/bytecode.h @@ -144,8 +144,9 @@ typedef union * and Fcc per draft-ietf-extra-sieve-mailboxid-01 * version 0x1E scripts store [current]date :zone argument as a string * version 0x1F scripts implemented vnd.cyrus.imip - */ -#define BYTECODE_VERSION 0x1F + * version 0x20 scripts implemented vnd.cyrus.implicit_keep_target +*/ +#define BYTECODE_VERSION 0x20 #define BYTECODE_MIN_VERSION 0x03 /* minimum supported version */ #define BYTECODE_MAGIC "CyrSBytecode" #define BYTECODE_MAGIC_LEN 12 /* Should be multiple of 4 */ @@ -434,6 +435,12 @@ enum bytecode { */ + B_IKEEP_TARGET, /* require ["vnd.cyrus.implicit_keep_target", + "special-use", "mailboxid"] + + + */ + /***** insert new actions above this line *****/ B_ILLEGAL_VALUE /* any value >= this code is illegal */ }; diff --git a/sieve/interp.c b/sieve/interp.c index 58176a3f00..e57df4d4c2 100644 --- a/sieve/interp.c +++ b/sieve/interp.c @@ -139,6 +139,8 @@ EXPORTED const strarray_t *sieve_listextensions(sieve_interp_t *i) if (i->imip && (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_IMIP)) buf_appendcstr(&buf, " vnd.cyrus.imip"); + if (config_sieve_extensions & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_IMPLICIT_KEEP_TARGET) + buf_appendcstr(&buf, " vnd.cyrus.implicit_keep_target"); /* add tests */ if (i->getenvelope && @@ -556,6 +558,9 @@ static const struct sieve_capa_t { /* iMIP - vnd.cyrus.imip */ { "vnd.cyrus.imip", SIEVE_CAPA_IMIP }, + /* vnd.cyrus.implicit_keep_target */ + { "vnd.cyrus.implicit_keep_target", SIEVE_CAPA_IKEEP_TARGET }, + { NULL, 0 } }; @@ -760,6 +765,11 @@ unsigned long long extension_isactive(sieve_interp_t *interp, const char *str) (config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_IMIP))) capa = 0; break; + case SIEVE_CAPA_IKEEP_TARGET: + if (!(config_ext & IMAP_ENUM_SIEVE_EXTENSIONS_VND_CYRUS_IMPLICIT_KEEP_TARGET)) + capa = 0; + break; + default: /* not supported */ capa = 0; diff --git a/sieve/interp.h b/sieve/interp.h index af79167254..b8819d2c82 100644 --- a/sieve/interp.h +++ b/sieve/interp.h @@ -90,8 +90,14 @@ struct sieve_interp { /* time when allocated */ time_t time; - /* have we addedd/deleted any headers? */ + /* have we added/deleted any headers? */ unsigned edited_headers : 1; + + struct { + const char *folder; + const char *mailboxid; + const char *specialuse; + } ikeep; }; @@ -219,6 +225,9 @@ enum sieve_capa_flag { /* iMIP - vnd.cyrus.imip */ SIEVE_CAPA_IMIP = 1LL<<50, + + /* vnd.cyrus.implicit_keep_target */ + SIEVE_CAPA_IKEEP_TARGET = 1LL<<51, }; #define SIEVE_CAPA_ALL (SIEVE_CAPA_BASE \ @@ -271,6 +280,7 @@ enum sieve_capa_flag { | SIEVE_CAPA_JMAPQUERY \ | SIEVE_CAPA_SNOOZE \ | SIEVE_CAPA_IMIP \ + | SIEVE_CAPA_IKEEP_TARGET \ ) #define SIEVE_CAPA_IHAVE_INCOMPAT (SIEVE_CAPA_ENCODED_CHAR | SIEVE_CAPA_VARIABLES) diff --git a/sieve/sieve-lex.l b/sieve/sieve-lex.l index 271c12b49a..4847aa0e6b 100644 --- a/sieve/sieve-lex.l +++ b/sieve/sieve-lex.l @@ -397,6 +397,11 @@ ws [ \t]+ :outcome return OUTCOME; :errstr return ERRSTR; + /* vnd.cyrus.implicit_keep_target */ +implicit_keep_target return IKEEP_TARGET; + /* :mailboxid defined in 'mailboxid' */ + /* :specialuse defined in 'special-use' */ + "/*"([^\*]|\*[^\/])*\*?"*/" ; /* ignore bracketed comments */ #.* ; /* ignore hash comments */ [ \t\n\r] ; /* ignore whitespace */ diff --git a/sieve/sieve.y b/sieve/sieve.y index c5d562a690..06742ea37e 100644 --- a/sieve/sieve.y +++ b/sieve/sieve.y @@ -120,6 +120,8 @@ static commandlist_t *build_log(sieve_script_t*, char *text); static commandlist_t *build_snooze(sieve_script_t *sscript, commandlist_t *c, arrayu64_t *times); static commandlist_t *build_imip(sieve_script_t *sscript, commandlist_t *t); +static commandlist_t *build_ikeep_target(sieve_script_t*, + commandlist_t *c, char *folder); /* construct/canonicalize test commands */ static test_t *build_anyof(sieve_script_t*, testlist_t *tl); @@ -349,6 +351,10 @@ extern void sieverestart(FILE *f); %token OUTCOME ERRSTR %type imiptags +/* vnd.cyrus.implicit_keep_target */ +%token IKEEP_TARGET +%type ikttags + %% @@ -526,6 +532,9 @@ action: KEEP ktags { $$ = build_keep(sscript, $2); } | LOG string { $$ = build_log(sscript, $2); } | SNOOZE sntags timelist { $$ = build_snooze(sscript, $2, $3); } | PROCESSIMIP imiptags { $$ = build_imip(sscript, $2); } + + | IKEEP_TARGET ikttags string + { $$ = build_ikeep_target(sscript, $2, $3); } ; @@ -564,8 +573,8 @@ ftags: /* empty */ { copy = &($$->u.f.copy); flags = &($$->u.f.flags); create = &($$->u.f.create); - specialuse = &($$->u.f.specialuse); - mailboxid = &($$->u.f.mailboxid); + specialuse = &($$->u.f.t.specialuse); + mailboxid = &($$->u.f.t.mailboxid); } | ftags copy | ftags flags @@ -884,9 +893,9 @@ vtags: /* empty */ { $$ = new_command(B_VACATION, sscript); flags = &($$->u.v.fcc.flags); create = &($$->u.v.fcc.create); - specialuse = &($$->u.v.fcc.specialuse); - mailboxid = &($$->u.v.fcc.mailboxid); - fccfolder = &($$->u.v.fcc.folder); + specialuse = &($$->u.v.fcc.t.specialuse); + mailboxid = &($$->u.v.fcc.t.mailboxid); + fccfolder = &($$->u.v.fcc.t.folder); } | vtags DAYS NUMBER { if ($$->u.v.seconds != -1) { @@ -1059,9 +1068,9 @@ ntags: /* empty */ { $$ = new_command(B_ENOTIFY, sscript); flags = &($$->u.n.fcc.flags); create = &($$->u.n.fcc.create); - specialuse = &($$->u.n.fcc.specialuse); - mailboxid = &($$->u.n.fcc.mailboxid); - fccfolder = &($$->u.n.fcc.folder); + specialuse = &($$->u.n.fcc.t.specialuse); + mailboxid = &($$->u.n.fcc.t.mailboxid); + fccfolder = &($$->u.n.fcc.t.folder); } /* enotify-only tagged arguments */ @@ -1179,18 +1188,18 @@ dtags: /* empty */ { sntags: /* empty */ { $$ = new_command(B_SNOOZE, sscript); create = &($$->u.sn.f.create); - specialuse = &($$->u.sn.f.specialuse); - mailboxid = &($$->u.sn.f.mailboxid); + specialuse = &($$->u.sn.f.t.specialuse); + mailboxid = &($$->u.sn.f.t.mailboxid); } | sntags MAILBOX string { - if ($$->u.sn.f.folder != NULL) { + if ($$->u.sn.f.t.folder != NULL) { sieveerror_c(sscript, SIEVE_DUPLICATE_TAG, ":mailbox"); - free($$->u.sn.f.folder); + free($$->u.sn.f.t.folder); } - $$->u.sn.f.folder = $3; + $$->u.sn.f.t.folder = $3; } | sntags create | sntags specialuse @@ -1399,6 +1408,17 @@ imiptags: /* empty */ { $$ = new_command(B_PROCESSIMIP, sscript); } ; +/* IKEEP_TARGET tagged arguments */ +ikttags: /* empty */ { + $$ = new_command(B_IKEEP_TARGET, sscript); + specialuse = &($$->u.ikt.specialuse); + mailboxid = &($$->u.ikt.mailboxid); + } + | ikttags specialuse + | ikttags mailboxid + ; + + /* * Test commands */ @@ -2470,15 +2490,15 @@ static commandlist_t *build_fileinto(sieve_script_t *sscript, strarray_add(c->u.f.flags, ""); } verify_mailbox(sscript, folder); - c->u.f.folder = folder; + c->u.f.t.folder = folder; c->nargs = bc_precompile(c->args, "ssiSis", - c->u.f.mailboxid, - c->u.f.specialuse, + c->u.f.t.mailboxid, + c->u.f.t.specialuse, c->u.f.create, c->u.f.flags, c->u.f.copy, - c->u.f.folder); + c->u.f.t.folder); return c; } @@ -2613,14 +2633,14 @@ static commandlist_t *build_vacation(sieve_script_t *sscript, verify_utf8(sscript, message); c->u.v.mime = 0; } - if (c->u.v.fcc.folder) { - verify_mailbox(sscript, c->u.v.fcc.folder); + if (c->u.v.fcc.t.folder) { + verify_mailbox(sscript, c->u.v.fcc.t.folder); if (c->u.v.fcc.flags && !_verify_flaglist(c->u.v.fcc.flags)) { strarray_add(c->u.v.fcc.flags, ""); } } else if (c->u.v.fcc.create || c->u.v.fcc.flags || - c->u.v.fcc.specialuse || c->u.v.fcc.mailboxid) { + c->u.v.fcc.t.specialuse || c->u.v.fcc.t.mailboxid) { sieveerror_c(sscript, SIEVE_MISSING_TAG, ":fcc"); } @@ -2631,7 +2651,7 @@ static commandlist_t *build_vacation(sieve_script_t *sscript, if (c->u.v.seconds > max) c->u.v.seconds = max; c->nargs = bc_precompile(c->args, - c->u.v.fcc.folder ? "SssiisssiSss" : "Sssiisss", + c->u.v.fcc.t.folder ? "SssiisssiSss" : "Sssiisss", c->u.v.addresses, c->u.v.subject, c->u.v.message, @@ -2639,11 +2659,11 @@ static commandlist_t *build_vacation(sieve_script_t *sscript, c->u.v.mime, c->u.v.from, c->u.v.handle, - c->u.v.fcc.folder, + c->u.v.fcc.t.folder, c->u.v.fcc.create, c->u.v.fcc.flags, - c->u.v.fcc.specialuse, - c->u.v.fcc.mailboxid); + c->u.v.fcc.t.specialuse, + c->u.v.fcc.t.mailboxid); return c; } @@ -2756,7 +2776,7 @@ static commandlist_t *build_snooze(sieve_script_t *sscript, { assert(c && c->type == B_SNOOZE); - if (c->u.sn.f.folder) verify_mailbox(sscript, c->u.sn.f.folder); + if (c->u.sn.f.t.folder) verify_mailbox(sscript, c->u.sn.f.t.folder); if (c->u.sn.addflags && !_verify_flaglist(c->u.sn.addflags)) { strarray_add(c->u.sn.addflags, ""); } @@ -2770,9 +2790,9 @@ static commandlist_t *build_snooze(sieve_script_t *sscript, c->u.sn.times = times; c->nargs = bc_precompile(c->args, "sssiSSisU", - c->u.sn.f.folder, - c->u.sn.f.mailboxid, - c->u.sn.f.specialuse, + c->u.sn.f.t.folder, + c->u.sn.f.t.mailboxid, + c->u.sn.f.t.specialuse, c->u.sn.f.create, c->u.sn.addflags, c->u.sn.removeflags, @@ -2815,14 +2835,14 @@ static commandlist_t *build_notify(sieve_script_t *sscript, int t, if (c->u.n.method != NULL) { sieveerror_c(sscript, SIEVE_UNEXPECTED_TAG, ":method"); } - if (c->u.n.fcc.folder) { - verify_mailbox(sscript, c->u.n.fcc.folder); + if (c->u.n.fcc.t.folder) { + verify_mailbox(sscript, c->u.n.fcc.t.folder); if (c->u.n.fcc.flags && !_verify_flaglist(c->u.n.fcc.flags)) { strarray_add(c->u.n.fcc.flags, ""); } } else if (c->u.n.fcc.create || c->u.n.fcc.flags || - c->u.n.fcc.specialuse || c->u.n.fcc.mailboxid) { + c->u.n.fcc.t.specialuse || c->u.n.fcc.t.mailboxid) { sieveerror_c(sscript, SIEVE_MISSING_TAG, ":fcc"); } @@ -2835,7 +2855,7 @@ static commandlist_t *build_notify(sieve_script_t *sscript, int t, if (c->u.n.from != NULL) { sieveerror_c(sscript, SIEVE_UNEXPECTED_TAG, ":from"); } - if (c->u.n.fcc.folder != NULL) { + if (c->u.n.fcc.t.folder != NULL) { sieveerror_c(sscript, SIEVE_UNEXPECTED_TAG, ":fcc"); } if (c->u.n.fcc.create != 0) { @@ -2844,7 +2864,7 @@ static commandlist_t *build_notify(sieve_script_t *sscript, int t, if (c->u.n.fcc.flags != NULL) { sieveerror_c(sscript, SIEVE_UNEXPECTED_TAG, ":flags"); } - if (c->u.n.fcc.specialuse != NULL) { + if (c->u.n.fcc.t.specialuse != NULL) { sieveerror_c(sscript, SIEVE_UNEXPECTED_TAG, ":specialuse"); } @@ -2949,6 +2969,22 @@ static commandlist_t *build_imip(sieve_script_t *sscript, commandlist_t *c) return c; } +static commandlist_t *build_ikeep_target(sieve_script_t *sscript, + commandlist_t *c, char *folder) +{ + assert(c && c->type == B_IKEEP_TARGET); + + verify_mailbox(sscript, folder); + c->u.ikt.folder = folder; + + c->nargs = bc_precompile(c->args, "sss", + c->u.ikt.mailboxid, + c->u.ikt.specialuse, + c->u.ikt.folder); + + return c; +} + static test_t *build_anyof(sieve_script_t *sscript, testlist_t *tl) { test_t *t; diff --git a/sieve/sieved.c b/sieve/sieved.c index d5b59635f6..108506bae1 100644 --- a/sieve/sieved.c +++ b/sieve/sieved.c @@ -604,16 +604,16 @@ static void dump2(bytecode_input_t *d, int bc_len) printf("\n\tCREATE(%d)", cmd.u.f.create); if (cmd.type >= B_FILEINTO_SPECIALUSE) { - print_string(" SPECIALUSE", cmd.u.f.specialuse); + print_string(" SPECIALUSE", cmd.u.f.t.specialuse); if (cmd.type >= B_FILEINTO) { - print_string(" MAILBOXID", cmd.u.f.mailboxid); + print_string(" MAILBOXID", cmd.u.f.t.mailboxid); } } } } } - print_string(" FOLDER", cmd.u.f.folder); + print_string(" FOLDER", cmd.u.f.t.folder); break; @@ -732,19 +732,19 @@ static void dump2(bytecode_input_t *d, int bc_len) print_string(" HANDLE", cmd.u.v.handle); if (cmd.type >= B_VACATION_FCC_ORIG) { - print_string("\n\tFCC", cmd.u.v.fcc.folder); + print_string("\n\tFCC", cmd.u.v.fcc.t.folder); - if (cmd.u.v.fcc.folder) { + if (cmd.u.v.fcc.t.folder) { printf(" CREATE(%d)", cmd.u.v.fcc.create); print_stringlist(" FLAGS", cmd.u.v.fcc.flags); if (cmd.type >= B_VACATION_FCC_SPLUSE) { print_string("\n\tSPECIALUSE", - cmd.u.v.fcc.specialuse); + cmd.u.v.fcc.t.specialuse); if (cmd.type >= B_VACATION) { print_string(" MAILBOXID", - cmd.u.v.fcc.mailboxid); + cmd.u.v.fcc.t.mailboxid); } } } @@ -823,14 +823,14 @@ static void dump2(bytecode_input_t *d, int bc_len) printf("SNOOZE"); if (cmd.type >= B_SNOOZE_TZID) { if (cmd.type >= B_SNOOZE) { - print_string(" MAILBOX", cmd.u.sn.f.folder); - print_string(" MAILBOXID", cmd.u.sn.f.mailboxid); - print_string(" SPECIALUSE", cmd.u.sn.f.specialuse); + print_string(" MAILBOX", cmd.u.sn.f.t.folder); + print_string(" MAILBOXID", cmd.u.sn.f.t.mailboxid); + print_string(" SPECIALUSE", cmd.u.sn.f.t.specialuse); printf(" CREATE(%d)", cmd.u.sn.f.create); } else { print_string(cmd.u.sn.is_mboxid ? " MAILBOXID" : " MAILBOX", - cmd.u.sn.f.folder); + cmd.u.sn.f.t.folder); } print_string(" TZID", cmd.u.sn.tzid); @@ -860,6 +860,15 @@ static void dump2(bytecode_input_t *d, int bc_len) print_string(" ERRSTR", cmd.u.imip.errstr_var); break; + + case B_IKEEP_TARGET: + printf("IMPLICIT_KEEP_TARGET"); + print_string(" MAILBOXID", cmd.u.ikt.mailboxid); + print_string(" SPECIALUSE", cmd.u.ikt.specialuse); + print_string(" FOLDER", cmd.u.ikt.folder); + break; + + default: printf("%d (NOT AN OP)\n", cmd.type); exit(1); @@ -1055,22 +1064,22 @@ static void generate_fileinto(struct Fileinto *f, { if (is_fcc) { /* Put :fcc first */ - generate_string(":fcc", f->folder, buf); + generate_string(":fcc", f->t.folder, buf); *requires |= SIEVE_CAPA_FCC; } generate_switch_capa(":copy", f->copy, SIEVE_CAPA_COPY, requires, buf); generate_switch_capa(":create", f->create, SIEVE_CAPA_MAILBOX, requires, buf); - generate_string_capa(":specialuse", f->specialuse, + generate_string_capa(":specialuse", f->t.specialuse, SIEVE_CAPA_SPECIAL_USE, requires, buf); - generate_string_capa(":mailboxid", f->mailboxid, + generate_string_capa(":mailboxid", f->t.mailboxid, SIEVE_CAPA_MAILBOXID, requires, buf); generate_stringlist_capa(":flags", f->flags, SIEVE_CAPA_IMAP4FLAGS, requires, buf); if (!is_fcc) { /* folder is positional and MUST be last for fileinto */ - generate_string(NULL, f->folder, buf); + generate_string(NULL, f->t.folder, buf); } } @@ -1538,7 +1547,7 @@ static int generate_block(bytecode_input_t *bc, int pos, int end, generate_string(":from", cmd.u.v.from, buf); generate_string(":handle", cmd.u.v.handle, buf); generate_stringlist(":addresses", cmd.u.v.addresses, buf); - if (cmd.u.v.fcc.folder) { + if (cmd.u.v.fcc.t.folder) { INSERT_FOLD(indent + 4, buf); generate_fileinto(&cmd.u.v.fcc, 1, requires, buf); } @@ -1615,15 +1624,15 @@ static int generate_block(bytecode_input_t *bc, int pos, int end, case B_SNOOZE: *requires |= SIEVE_CAPA_SNOOZE; if (cmd.u.sn.is_mboxid) { - cmd.u.sn.f.mailboxid = cmd.u.sn.f.folder; - cmd.u.sn.f.folder = NULL; + cmd.u.sn.f.t.mailboxid = cmd.u.sn.f.t.folder; + cmd.u.sn.f.t.folder = NULL; } generate_token("snooze", indent, buf); - generate_string(":mailbox", cmd.u.sn.f.folder, buf); - generate_string_capa(":mailboxid", cmd.u.sn.f.mailboxid, + generate_string(":mailbox", cmd.u.sn.f.t.folder, buf); + generate_string_capa(":mailboxid", cmd.u.sn.f.t.mailboxid, SIEVE_CAPA_MAILBOXID, requires, buf); - generate_string_capa(":specialuse", cmd.u.sn.f.specialuse, + generate_string_capa(":specialuse", cmd.u.sn.f.t.specialuse, SIEVE_CAPA_SPECIAL_USE, requires, buf); generate_switch_capa(":create", cmd.u.sn.f.create, SIEVE_CAPA_MAILBOX, requires, buf); @@ -1660,6 +1669,16 @@ static int generate_block(bytecode_input_t *bc, int pos, int end, generate_string(":errstr", cmd.u.imip.errstr_var, buf); break; + case B_IKEEP_TARGET: + *requires |= SIEVE_CAPA_IKEEP_TARGET; + generate_token("implicit_keep_target", indent, buf); + generate_string_capa(":mailboxid", cmd.u.ikt.mailboxid, + SIEVE_CAPA_MAILBOXID, requires, buf); + generate_string_capa(":specialuse", cmd.u.ikt.specialuse, + SIEVE_CAPA_SPECIAL_USE, requires, buf); + generate_string(NULL, cmd.u.ikt.folder, buf); + break; + default: fprintf(stderr, "%d (NOT AN OP)\n", cmd.type); exit(1); diff --git a/sieve/tree.c b/sieve/tree.c index b6c3fc51a2..899eb9404f 100644 --- a/sieve/tree.c +++ b/sieve/tree.c @@ -324,6 +324,10 @@ commandlist_t *new_command(int type, sieve_script_t *parse_script) capability = "vnd.cyrus.imip"; supported = parse_script->support & SIEVE_CAPA_IMIP; break; + + case B_IKEEP_TARGET: + capability = "vnd.cyrus.implicit_keep_target"; + supported = parse_script->support & SIEVE_CAPA_IKEEP_TARGET; } if (!supported) { @@ -440,11 +444,16 @@ void free_test(test_t *t) free(t); } +static void free_target_mailbox(struct TargetMailbox *t) +{ + free(t->folder); + free(t->specialuse); + free(t->mailboxid); +} + static void free_fileinto(struct Fileinto *f) { - free(f->folder); - free(f->specialuse); - free(f->mailboxid); + free_target_mailbox(&f->t); strarray_free(f->flags); } @@ -554,6 +563,10 @@ void free_tree(commandlist_t *cl) free(cl->u.imip.outcome_var); free(cl->u.imip.errstr_var); break; + + case B_IKEEP_TARGET: + free_target_mailbox(&cl->u.ikt); + break; } free(cl); diff --git a/sieve/tree.h b/sieve/tree.h index 435cf6e9b9..3504a64cd4 100644 --- a/sieve/tree.h +++ b/sieve/tree.h @@ -159,13 +159,17 @@ struct Testlist { testlist_t *next; }; -struct Fileinto { - strarray_t *flags; +struct TargetMailbox { char *folder; char *specialuse; + char *mailboxid; +}; + +struct Fileinto { + struct TargetMailbox t; + strarray_t *flags; int copy; int create; - char *mailboxid; }; struct Commandlist { @@ -262,6 +266,7 @@ struct Commandlist { char *outcome_var; char *errstr_var; } imip; + struct TargetMailbox ikt; /* it's an implicit keep target */ } u; struct Commandlist *next;