Skip to content
Snippets Groups Projects
Commit 2cbeec33 authored by Stephan Bosch's avatar Stephan Bosch
Browse files

Sieve filter tool: finished basic functionality.

parent 015073d0
No related branches found
No related tags found
No related merge requests found
...@@ -2,8 +2,6 @@ Current activities: ...@@ -2,8 +2,6 @@ Current activities:
* Build a sieve tool to filter an entire existing mailbox through a Sieve * Build a sieve tool to filter an entire existing mailbox through a Sieve
script. script.
* Finish the ereject extension
- Make reject/ereject use the LDA reject interface when available
Next (in order of descending priority/precedence): Next (in order of descending priority/precedence):
...@@ -24,6 +22,7 @@ Next (in order of descending priority/precedence): ...@@ -24,6 +22,7 @@ Next (in order of descending priority/precedence):
* Implement index extension * Implement index extension
* Update include extension to latest draft (v05 currently): * Update include extension to latest draft (v05 currently):
- Implement required ManageSieve behavior (pending IETF discussion) - Implement required ManageSieve behavior (pending IETF discussion)
* Finish the ereject extension
* Vacation extension improvements: * Vacation extension improvements:
- Implement configurable sender exclusion list. - Implement configurable sender exclusion list.
- Implement mechanism for implicitly including an account's aliases in the - Implement mechanism for implicitly including an account's aliases in the
......
...@@ -26,7 +26,7 @@ However, there are occasions when it is desirable to filter messages that are ...@@ -26,7 +26,7 @@ However, there are occasions when it is desirable to filter messages that are
already stored in a mailbox, for instance when a bug in a Sieve script caused already stored in a mailbox, for instance when a bug in a Sieve script caused
many messages to be delivered incorrectly. Using the sieve\-filter tool it is many messages to be delivered incorrectly. Using the sieve\-filter tool it is
possible to apply a Sieve script on all messages in a particular mailbox, making possible to apply a Sieve script on all messages in a particular mailbox, making
it possible to delete messages, to store them in a different folder and to it possible to delete messages, to store them in a different mailbox and to
change the assigned IMAP flags and keywords. Attempts to send messages to the change the assigned IMAP flags and keywords. Attempts to send messages to the
outside world are ignored by default for obvious reasons, but, using the proper outside world are ignored by default for obvious reasons, but, using the proper
command line options, it is possible to capture outgoing mail as well. command line options, it is possible to capture outgoing mail as well.
...@@ -35,7 +35,10 @@ If no options are specified, the sieve\-filter command runs in a simulation mode ...@@ -35,7 +35,10 @@ If no options are specified, the sieve\-filter command runs in a simulation mode
in which it only prints what would be performed, without actually doing in which it only prints what would be performed, without actually doing
anything. Use the \fB\-e\fP option to activate true script execution. Also, the anything. Use the \fB\-e\fP option to activate true script execution. Also, the
source mailbox is opened read\-only by default, so that the source mailbox source mailbox is opened read\-only by default, so that the source mailbox
remains unchanged. Use the \fB\-W\fP to allow changes in the source mailbox. remains unchanged. Use the \fB\-W\fP option to allow changes in the source mailbox.
And even with the \fB\-W\fP option enabled, messages in the source mailbox are
only potentially modified and not (re)moved, unless a \fIsource\-action\fP
argument other than \fBkeep\fP is specified.
.SS CAUTION .SS CAUTION
Although this is a very useful tool, it can also be very destructive when used Although this is a very useful tool, it can also be very destructive when used
improperly. A small bug in your Sieve script in combination with the wrong improperly. A small bug in your Sieve script in combination with the wrong
...@@ -73,17 +76,28 @@ Using this option the sieve\-filter command becomes active and performs the ...@@ -73,17 +76,28 @@ Using this option the sieve\-filter command becomes active and performs the
requested actions. requested actions.
.TP .TP
.BI \-m\ default\-mailbox .BI \-m\ default\-mailbox
The mailbox where the keep action stores messages. This is \(dqINBOX\(dq The mailbox where the (implicit) \fBkeep\fP Sieve action stores messages. This is
by default. equal to the source mailbox by default.
.TP .TP
.BI \-q\ output\-mailbox .B \-M
Enable move mode. This will cause all messages that were succesfully stored
somewhere else to be expunged from the source mailbox, regardless of what the
\fIsource\-action\fP is (refer to Arguments section below). However, if the
Sieve filter decides to keep the message in the source mailbox, it is left
there and not affected by this option.
.IP
Note that some Sieve actions, such as \fBredirect\fP, don't store the message
anywhere and are thus \- with respect to the fate of the message in the source
mailbox \- treated as if a \fBdiscard\fP action were executed.
.TP
.BI \-q\ output\-mailbox\ \fR[not\ implemented\ yet]\fP
Store outgoing e\-mail into the indicated \fIoutput\-mailbox\fP. By default, Store outgoing e\-mail into the indicated \fIoutput\-mailbox\fP. By default,
the sieve\-filter command ignores Sieve actions such as redirect, reject, the sieve\-filter command ignores Sieve actions such as redirect, reject,
vacation and notify, but using this option outgoing messages can be appended to vacation and notify, but using this option outgoing messages can be appended to
the indicated mailbox. This option has no effect in simulation mode. Flags of the indicated mailbox. This option has no effect in simulation mode. Flags of
redirected messages are not preserved. redirected messages are not preserved.
.TP .TP
.BI \-Q\ mail\-command .BI \-Q\ mail\-command\ \fR[not\ implemented\ yet]\fP
Send outgoing e\-mail (e.g. as produced by redirect, reject and vacation) Send outgoing e\-mail (e.g. as produced by redirect, reject and vacation)
through the specified program. By default, the sieve\-filter command ignores through the specified program. By default, the sieve\-filter command ignores
Sieve actions such as redirect, reject, vacation and notify, but using this Sieve actions such as redirect, reject, vacation and notify, but using this
...@@ -91,7 +105,7 @@ option outgoing messages can be fed to the \fBstdin\fP of an external shell ...@@ -91,7 +105,7 @@ option outgoing messages can be fed to the \fBstdin\fP of an external shell
command. This option has no effect in simulation mode. Unless you really know command. This option has no effect in simulation mode. Unless you really know
what you are doing, \fBDO NOT USE THIS TO FEED MAIL TO SENDMAIL!\fP. what you are doing, \fBDO NOT USE THIS TO FEED MAIL TO SENDMAIL!\fP.
.TP .TP
.BI \-s\ script\-file .BI \-s\ script\-file\ \fR[not\ implemented\ yet]\fP
Specify additional scripts to be executed before the main script. Multiple Specify additional scripts to be executed before the main script. Multiple
\fB\-s\fP arguments are allowed and the specified scripts are executed \fB\-s\fP arguments are allowed and the specified scripts are executed
sequentially in the order specified at the command sequentially in the order specified at the command
...@@ -133,21 +147,18 @@ into a new binary. ...@@ -133,21 +147,18 @@ into a new binary.
The name of the source mailbox. The name of the source mailbox.
.TP .TP
.I source\-action .I source\-action
Specifies what needs to be done with messages in the source mailbox once Specifies what is done with messages in the source mailbox once processed by the
processed by the Sieve script. By default, the sieve\-filter command does not Sieve script. The \fIsource\-action\fP parameter accepts one of the following values:
remove the messages from the source mailbox. In particular this means that the
keep and fileinto actions will cause the messages to be copied. To alter this
behavior, the \fIsource\-action\fP parameter of the \fB\-R\fP option accepts
one of the following values:
.RS 7 .RS 7
.TP .TP
.BR keep\ (default) .BR keep\ (default)
Keep processed messages in source folder. If \fB\-W\fR is specified and the Keep processed messages in source mailbox. When the filter decides to store the message
source mailbox is the destination of a keep or fileinto action, flags may be in the source mailbox, it is never duplicated there. However, in that case, the
changed by the Sieve script, but messages are never duplicated there. IMAP flags of the original message can be modified by the Sieve interpreter.
.TP .TP
.BI move\ folder .BI move\ mailbox
Move processed messages to the indicated \fIfolder\fP. Move processed messages to the indicated \fImailbox\fP. This is for instance useful
to move messages to a Trash mailbox.
.TP .TP
.B delete .B delete
Flag processed messages as \\DELETED. Flag processed messages as \\DELETED.
...@@ -155,9 +166,21 @@ Flag processed messages as \\DELETED. ...@@ -155,9 +166,21 @@ Flag processed messages as \\DELETED.
.B expunge .B expunge
Expunge processed messages, meaning that these are removed irreversibly when the Expunge processed messages, meaning that these are removed irreversibly when the
tool finishes filtering. tool finishes filtering.
.PP .RE
Note that the chosen \fIsource\-action\fP only has an effect on the source .IP
mailbox when the \fB\-W\fP option is specified as well. The \fBsource\-action\fP is normally applied to all messages in the source mailbox,
but there are a few exceptions:
.RS 7
.IP \(bu
When the \fB\-W\fP option is not specified, the source mailbox is immutable and
the specified \fIsource\-action\fP has no effect.
.IP \(bu
When the \fB-M\fR option (move mode) is active, all messages that were
successfully moved to another mailbox are expunged irrespective of the specified
\fIsource\-action\fP.
.IP \(bu
If the filter decides to keep the message in the source mailbox, it is left there
and not affected by the \fIsource\-action\fP.
.RE .RE
.\"------------------------------------------------------------------------ .\"------------------------------------------------------------------------
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "ioloop.h" #include "ioloop.h"
#include "env-util.h" #include "env-util.h"
#include "str.h" #include "str.h"
#include "str-sanitize.h"
#include "ostream.h" #include "ostream.h"
#include "array.h" #include "array.h"
#include "mail-namespace.h" #include "mail-namespace.h"
...@@ -49,30 +50,46 @@ enum sieve_filter_source_action { ...@@ -49,30 +50,46 @@ enum sieve_filter_source_action {
SIEVE_FILTER_SACT_EXPUNGE /* Expunge discarded messages */ SIEVE_FILTER_SACT_EXPUNGE /* Expunge discarded messages */
}; };
struct sieve_filter_context { struct sieve_filter_data {
enum sieve_filter_source_action source_action; enum sieve_filter_source_action source_action;
const char *move_mailbox; struct mailbox *move_mailbox;
struct sieve_script_env *senv; struct sieve_script_env *senv;
struct sieve_binary *main_sbin; struct sieve_binary *main_sbin;
struct sieve_error_handler *ehandler; struct sieve_error_handler *ehandler;
unsigned int execute:1;
unsigned int source_write:1;
unsigned int move_mode:1;
};
struct sieve_filter_context {
const struct sieve_filter_data *data;
struct mailbox_transaction_context *move_trans;
struct ostream *teststream;
}; };
static int filter_message static int filter_message
(struct sieve_filter_context *sfctx, struct mail *mail) (struct sieve_filter_context *sfctx, struct mail *mail)
{ {
struct sieve_error_handler *ehandler = sfctx->ehandler; struct sieve_error_handler *ehandler = sfctx->data->ehandler;
struct sieve_script_env *senv = sfctx->data->senv;
struct sieve_exec_status estatus; struct sieve_exec_status estatus;
struct sieve_binary *sbin; struct sieve_binary *sbin;
struct sieve_message_data msgdata; struct sieve_message_data msgdata;
const char *recipient, *sender; const char *recipient, *sender;
bool execute = sfctx->data->execute;
bool source_write = sfctx->data->source_write;
bool move_mode = sfctx->data->move_mode;
int ret; int ret;
sieve_tool_get_envelope_data(mail, &recipient, &sender); sieve_tool_get_envelope_data(mail, &recipient, &sender);
/* Initialize execution status */ /* Initialize execution status */
memset(&estatus, 0, sizeof(estatus)); memset(&estatus, 0, sizeof(estatus));
sfctx->senv->exec_status = &estatus; senv->exec_status = &estatus;
/* Collect necessary message data */ /* Collect necessary message data */
memset(&msgdata, 0, sizeof(msgdata)); memset(&msgdata, 0, sizeof(msgdata));
...@@ -80,44 +97,91 @@ static int filter_message ...@@ -80,44 +97,91 @@ static int filter_message
msgdata.return_path = sender; msgdata.return_path = sender;
msgdata.orig_envelope_to = recipient; msgdata.orig_envelope_to = recipient;
msgdata.final_envelope_to = recipient; msgdata.final_envelope_to = recipient;
msgdata.auth_user = sfctx->senv->username; msgdata.auth_user = senv->username;
(void)mail_get_first_header(mail, "Message-ID", &msgdata.id); (void)mail_get_first_header(mail, "Message-ID", &msgdata.id);
/* Single script */ /* Single script */
sbin = sfctx->main_sbin; sbin = sfctx->data->main_sbin;
/* Execute script */ /* Execute script */
ret = sieve_execute(sbin, &msgdata, sfctx->senv, ehandler, NULL); if ( execute ) {
ret = sieve_execute(sbin, &msgdata, senv, ehandler, NULL);
} else {
ret = sieve_test
(sbin, &msgdata, senv, ehandler, sfctx->teststream, NULL);
}
/* Handle message in source folder */ /* Handle message in source folder */
if ( ret > 0 && !estatus.keep_original ) { if ( ret > 0 ) {
switch ( sfctx->source_action ) { struct mailbox *move_box = sfctx->data->move_mailbox;
/* Leave it there */ enum sieve_filter_source_action source_action =
case SIEVE_FILTER_SACT_KEEP: sfctx->data->source_action;
sieve_info(ehandler, NULL, "message left in source mailbox");
break; if ( !source_write ) {
/* Move message to Trash folder */ /* Do nothing */
case SIEVE_FILTER_SACT_MOVE:
sieve_info(ehandler, NULL, } else if ( estatus.keep_original ) {
"message in source mailbox moved to mailbox '%s'", sfctx->move_mailbox); sieve_info(ehandler, NULL, "message kept in source mailbox");
break;
/* Flag message as \DELETED */ } else if ( move_mode && estatus.message_saved ) {
case SIEVE_FILTER_SACT_DELETE: sieve_info(ehandler, NULL,
sieve_info(ehandler, NULL, "message flagged as deleted in source mailbox"); "message expunged from source mailbox upon successful move");
mail_update_flags(mail, MODIFY_ADD, MAIL_DELETED);
break; if ( execute )
/* Expunge the message immediately */ mail_expunge(mail);
case SIEVE_FILTER_SACT_EXPUNGE:
sieve_info(ehandler, NULL, "message expunged from source mailbox"); } else {
mail_expunge(mail); switch ( source_action ) {
break; /* Leave it there */
/* Unknown */ case SIEVE_FILTER_SACT_KEEP:
default: sieve_info(ehandler, NULL, "message left in source mailbox");
i_unreached(); break;
break; /* Move message to indicated folder */
case SIEVE_FILTER_SACT_MOVE:
sieve_info(ehandler, NULL,
"message in source mailbox moved to mailbox '%s'",
mailbox_get_name(move_box));
if ( execute && move_box != NULL ) {
struct mailbox_transaction_context *t = sfctx->move_trans;
struct mail_save_context *save_ctx;
save_ctx = mailbox_save_alloc(t);
if ( mailbox_copy(&save_ctx, mail) < 0 ) {
enum mail_error error;
const char *errstr;
errstr = mail_storage_get_last_error
(mailbox_get_storage(move_box), &error);
sieve_error(ehandler, NULL,
"failed to move message to mailbox %s: %s",
mailbox_get_name(move_box), errstr);
return -1;
}
mail_expunge(mail);
}
break;
/* Flag message as \DELETED */
case SIEVE_FILTER_SACT_DELETE:
sieve_info(ehandler, NULL, "message flagged as deleted in source mailbox");
if ( execute )
mail_update_flags(mail, MODIFY_ADD, MAIL_DELETED);
break;
/* Expunge the message immediately */
case SIEVE_FILTER_SACT_EXPUNGE:
sieve_info(ehandler, NULL, "message expunged from source mailbox");
if ( execute )
mail_expunge(mail);
break;
/* Unknown */
default:
i_unreached();
break;
}
} }
} else {
sieve_info(ehandler, NULL, "message left in source mailbox");
} }
return ret; return ret;
...@@ -139,32 +203,51 @@ static void mail_search_build_add_flags ...@@ -139,32 +203,51 @@ static void mail_search_build_add_flags
} }
static int filter_mailbox static int filter_mailbox
(struct sieve_filter_context *sfctx, struct mailbox *src_box) (const struct sieve_filter_data *sfdata, struct mailbox *src_box)
{ {
struct sieve_error_handler *ehandler = sfctx->ehandler; struct sieve_filter_context sfctx;
struct mailbox *move_box = sfdata->move_mailbox;
struct sieve_error_handler *ehandler = sfdata->ehandler;
struct mail_search_args *search_args; struct mail_search_args *search_args;
struct mailbox_transaction_context *t; struct mailbox_transaction_context *t;
struct mail_search_context *search_ctx; struct mail_search_context *search_ctx;
struct mail *mail; struct mail *mail;
int ret = 1; int ret = 1;
/* Sync mailbox */ /* Sync source mailbox */
if ( mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_READ) < 0 ) { if ( mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_READ) < 0 ) {
sieve_error(ehandler, NULL, "failed to sync source mailbox");
return -1; return -1;
} }
/* Initialize */
memset(&sfctx, 0, sizeof(sfctx));
sfctx.data = sfdata;
/* Create test stream */
if ( !sfdata->execute )
sfctx.teststream = o_stream_create_fd(1, 0, FALSE);
/* Start move mailbox transaction */
if ( move_box != NULL ) {
sfctx.move_trans = mailbox_transaction_begin
(move_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL);
}
/* Search non-deleted messages in the source folder */ /* Search non-deleted messages in the source folder */
search_args = mail_search_build_init(); search_args = mail_search_build_init();
mail_search_build_add_flags(search_args, MAIL_DELETED, TRUE); mail_search_build_add_flags(search_args, MAIL_DELETED, TRUE);
/* Iterate through all requested messages */
t = mailbox_transaction_begin(src_box, 0); t = mailbox_transaction_begin(src_box, 0);
search_ctx = mailbox_search_init(t, search_args, NULL); search_ctx = mailbox_search_init(t, search_args, NULL);
mail_search_args_unref(&search_args); mail_search_args_unref(&search_args);
/* Iterate through all requested messages */
mail = mail_alloc(t, 0, NULL); mail = mail_alloc(t, 0, NULL);
while ( ret > 0 && mailbox_search_next(search_ctx, mail) > 0 ) { while ( ret > 0 && mailbox_search_next(search_ctx, mail) > 0 ) {
const char *subject, *date; const char *subject, *date;
...@@ -186,9 +269,10 @@ static int filter_mailbox ...@@ -186,9 +269,10 @@ static int filter_mailbox
subject = ""; subject = "";
sieve_info(ehandler, NULL, sieve_info(ehandler, NULL,
"filtering: [%s; %"PRIuUOFF_T" bytes] %s", date, size, subject); "filtering: [%s; %"PRIuUOFF_T" bytes] %s", date, size,
str_sanitize(subject, 40));
ret = filter_message(sfctx, mail); ret = filter_message(&sfctx, mail);
} }
mail_free(&mail); mail_free(&mail);
...@@ -198,19 +282,36 @@ static int filter_mailbox ...@@ -198,19 +282,36 @@ static int filter_mailbox
ret = -1; ret = -1;
} }
if ( sfctx.move_trans != NULL ) {
if ( ret < 0 ) {
mailbox_transaction_rollback(&sfctx.move_trans);
} else {
if ( mailbox_transaction_commit(&sfctx.move_trans) < 0 ) {
ret = -1;
}
}
}
if ( ret < 0 ) { if ( ret < 0 ) {
mailbox_transaction_rollback(&t); mailbox_transaction_rollback(&t);
return -1;
} else { } else {
if ( mailbox_transaction_commit(&t) < 0 ) { if ( mailbox_transaction_commit(&t) < 0 ) {
return -1; ret = -1;
} }
} }
if ( sfctx.teststream != NULL )
o_stream_destroy(&sfctx.teststream);
if ( ret < 0 ) return ret;
/* Sync mailbox */ /* Sync mailbox */
if ( mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_WRITE) < 0 ) { if ( sfdata->execute ) {
return -1; if ( mailbox_sync(src_box, MAILBOX_SYNC_FLAG_FULL_WRITE) < 0 ) {
sieve_error(ehandler, NULL, "failed to sync source mailbox");
return -1;
}
} }
return ret; return ret;
...@@ -235,30 +336,29 @@ int main(int argc, char **argv) ...@@ -235,30 +336,29 @@ int main(int argc, char **argv)
struct sieve_instance *svinst; struct sieve_instance *svinst;
ARRAY_TYPE (const_string) scriptfiles; ARRAY_TYPE (const_string) scriptfiles;
const char *scriptfile, *src_mailbox, *dst_mailbox, *move_mailbox; const char *scriptfile, *src_mailbox, *dst_mailbox, *move_mailbox;
struct sieve_filter_context sfctx; struct sieve_filter_data sfdata;
enum sieve_filter_source_action source_action = SIEVE_FILTER_SACT_KEEP; enum sieve_filter_source_action source_action = SIEVE_FILTER_SACT_KEEP;
struct mail_user *mail_user; struct mail_user *mail_user;
struct sieve_binary *main_sbin; struct sieve_binary *main_sbin;
struct sieve_script_env scriptenv; struct sieve_script_env scriptenv;
struct sieve_error_handler *ehandler; struct sieve_error_handler *ehandler;
bool force_compile = FALSE, execute = FALSE, source_write = FALSE; bool force_compile, execute, source_write, move_mode;
struct mail_namespace *ns; struct mail_namespace *ns;
struct mailbox *src_box; struct mailbox *src_box = NULL, *move_box = NULL;
enum mailbox_flags open_flags = enum mailbox_flags open_flags =
MAILBOX_FLAG_KEEP_RECENT | MAILBOX_FLAG_IGNORE_ACLS; MAILBOX_FLAG_KEEP_RECENT | MAILBOX_FLAG_IGNORE_ACLS;
enum mail_error error; enum mail_error error;
int c; int c;
sieve_tool = sieve_tool_init("sieve-filter", &argc, &argv, sieve_tool = sieve_tool_init("sieve-filter", &argc, &argv,
"R:m:s:x:P:CD", FALSE); "m:s:x:P:q:Q:DCeWM", FALSE);
t_array_init(&scriptfiles, 16); t_array_init(&scriptfiles, 16);
/* Parse arguments */ /* Parse arguments */
scriptfile = NULL; scriptfile = NULL;
src_mailbox = dst_mailbox = "INBOX"; src_mailbox = dst_mailbox = move_mailbox = NULL;
move_mailbox = "Trash"; force_compile = execute = source_write = move_mode = FALSE;
force_compile = FALSE;
while ((c = sieve_tool_getopt(sieve_tool)) > 0) { while ((c = sieve_tool_getopt(sieve_tool)) > 0) {
switch (c) { switch (c) {
case 'm': case 'm':
...@@ -278,22 +378,39 @@ int main(int argc, char **argv) ...@@ -278,22 +378,39 @@ int main(int argc, char **argv)
"The -s argument is currently NOT IMPLEMENTED"); "The -s argument is currently NOT IMPLEMENTED");
} }
break; break;
case 'q':
i_fatal_status(EX_USAGE,
"The -q argument is currently NOT IMPLEMENTED");
break;
case 'Q':
i_fatal_status(EX_USAGE,
"The -Q argument is currently NOT IMPLEMENTED");
break;
case 'e': case 'e':
/* execution mode */
execute = TRUE; execute = TRUE;
break; break;
case 'C': case 'C':
/* force script compile */
force_compile = TRUE; force_compile = TRUE;
break; break;
case 'M':
/* move mode */
move_mode = TRUE;
break;
case 'W': case 'W':
/* enable source mailbox write */
source_write = TRUE; source_write = TRUE;
break; break;
default: default:
/* unrecognized option */
print_help(); print_help();
i_fatal_status(EX_USAGE, "Unknown argument: %c", c); i_fatal_status(EX_USAGE, "Unknown argument: %c", c);
break; break;
} }
} }
/* Script file argument */
if ( optind < argc ) { if ( optind < argc ) {
scriptfile = t_strdup(argv[optind++]); scriptfile = t_strdup(argv[optind++]);
} else { } else {
...@@ -301,6 +418,7 @@ int main(int argc, char **argv) ...@@ -301,6 +418,7 @@ int main(int argc, char **argv)
i_fatal_status(EX_USAGE, "Missing <script-file> argument"); i_fatal_status(EX_USAGE, "Missing <script-file> argument");
} }
/* Source mailbox argument */
if ( optind < argc ) { if ( optind < argc ) {
src_mailbox = t_strdup(argv[optind++]); src_mailbox = t_strdup(argv[optind++]);
} else { } else {
...@@ -308,6 +426,7 @@ int main(int argc, char **argv) ...@@ -308,6 +426,7 @@ int main(int argc, char **argv)
i_fatal_status(EX_USAGE, "Missing <source-mailbox> argument"); i_fatal_status(EX_USAGE, "Missing <source-mailbox> argument");
} }
/* Source action argument */
if ( optind < argc ) { if ( optind < argc ) {
const char *srcact = argv[optind++]; const char *srcact = argv[optind++];
...@@ -317,6 +436,11 @@ int main(int argc, char **argv) ...@@ -317,6 +436,11 @@ int main(int argc, char **argv)
source_action = SIEVE_FILTER_SACT_MOVE; source_action = SIEVE_FILTER_SACT_MOVE;
if ( optind < argc ) { if ( optind < argc ) {
move_mailbox = t_strdup(argv[optind++]); move_mailbox = t_strdup(argv[optind++]);
} else {
print_help();
i_fatal_status(EX_USAGE,
"Invalid <source-action> argument: "
"the `move' action requires mailbox argument");
} }
} else if ( strcmp(srcact, "flag") == 0 ) { } else if ( strcmp(srcact, "flag") == 0 ) {
source_action = SIEVE_FILTER_SACT_DELETE; source_action = SIEVE_FILTER_SACT_DELETE;
...@@ -333,6 +457,11 @@ int main(int argc, char **argv) ...@@ -333,6 +457,11 @@ int main(int argc, char **argv)
i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]);
} }
if ( dst_mailbox == NULL ) {
dst_mailbox = src_mailbox;
}
/* Finish tool initialization */
svinst = sieve_tool_init_finish(sieve_tool); svinst = sieve_tool_init_finish(sieve_tool);
/* Register Sieve debug extension */ /* Register Sieve debug extension */
...@@ -355,24 +484,45 @@ int main(int argc, char **argv) ...@@ -355,24 +484,45 @@ int main(int argc, char **argv)
/* Initialize mail user */ /* Initialize mail user */
mail_user = sieve_tool_get_mail_user(sieve_tool); mail_user = sieve_tool_get_mail_user(sieve_tool);
/* Find namespace for source mailbox */ /* Open the source mailbox */
src_mailbox = mailbox_name_to_mutf7(src_mailbox); src_mailbox = mailbox_name_to_mutf7(src_mailbox);
ns = mail_namespace_find(mail_user->namespaces, &src_mailbox); ns = mail_namespace_find(mail_user->namespaces, &src_mailbox);
if ( ns == NULL ) if ( ns == NULL )
i_fatal("Unknown namespace for source mailbox '%s'", src_mailbox); i_fatal("Unknown namespace for source mailbox '%s'", src_mailbox);
/* Open the source mailbox */ if ( !source_write || !execute )
if ( !source_write )
open_flags |= MAILBOX_FLAG_READONLY; open_flags |= MAILBOX_FLAG_READONLY;
src_box = mailbox_alloc(ns->list, src_mailbox, open_flags); src_box = mailbox_alloc(ns->list, src_mailbox, open_flags);
if ( mailbox_open(src_box) < 0 ) { if ( mailbox_open(src_box) < 0 ) {
i_fatal("Couldn't open mailbox '%s': %s", i_fatal("Couldn't open source mailbox '%s': %s",
src_mailbox, mail_storage_get_last_error(ns->storage, &error)); src_mailbox, mail_storage_get_last_error(ns->storage, &error));
} }
/* Open move box if necessary */
if ( execute && source_action == SIEVE_FILTER_SACT_MOVE &&
move_mailbox != NULL ) {
move_mailbox = mailbox_name_to_mutf7(move_mailbox);
ns = mail_namespace_find(mail_user->namespaces, &move_mailbox);
if ( ns == NULL )
i_fatal("Unknown namespace for mailbox '%s'", move_mailbox);
move_box = mailbox_alloc(ns->list, move_mailbox, open_flags);
if ( mailbox_open(move_box) < 0 ) {
i_fatal("Couldn't open mailbox '%s': %s",
move_mailbox, mail_storage_get_last_error(ns->storage, &error));
}
if ( mailbox_backends_equal(src_box, move_box) ) {
i_fatal("Source mailbox and mailbox for move action are identical.");
}
}
/* Compose script environment */ /* Compose script environment */
memset(&scriptenv, 0, sizeof(scriptenv)); memset(&scriptenv, 0, sizeof(scriptenv));
scriptenv.mailbox_autocreate = TRUE; scriptenv.mailbox_autocreate = FALSE;
scriptenv.default_mailbox = dst_mailbox; scriptenv.default_mailbox = dst_mailbox;
scriptenv.user = mail_user; scriptenv.user = mail_user;
scriptenv.username = sieve_tool_get_username(sieve_tool); scriptenv.username = sieve_tool_get_username(sieve_tool);
...@@ -382,20 +532,27 @@ int main(int argc, char **argv) ...@@ -382,20 +532,27 @@ int main(int argc, char **argv)
scriptenv.smtp_close = NULL; scriptenv.smtp_close = NULL;
/* Compose filter context */ /* Compose filter context */
memset(&sfctx, 0, sizeof(sfctx)); memset(&sfdata, 0, sizeof(sfdata));
sfctx.senv = &scriptenv; sfdata.senv = &scriptenv;
sfctx.source_action = source_action; sfdata.source_action = source_action;
sfctx.move_mailbox = move_mailbox; sfdata.move_mailbox = move_box;
sfctx.main_sbin = main_sbin; sfdata.main_sbin = main_sbin;
sfctx.ehandler = ehandler; sfdata.ehandler = ehandler;
sfdata.execute = execute;
sfdata.source_write = source_write;
sfdata.move_mode = move_mode;
/* Apply Sieve filter to all messages found */ /* Apply Sieve filter to all messages found */
(void) filter_mailbox(&sfctx, src_box); (void) filter_mailbox(&sfdata, src_box);
/* Close the mailbox */ /* Close the source mailbox */
if ( src_box != NULL ) if ( src_box != NULL )
mailbox_free(&src_box); mailbox_free(&src_box);
/* Close the move mailbox */
if ( move_box != NULL )
mailbox_free(&move_box);
/* Cleanup error handler */ /* Cleanup error handler */
sieve_error_handler_unref(&ehandler); sieve_error_handler_unref(&ehandler);
......
...@@ -156,6 +156,7 @@ int main(int argc, char **argv) ...@@ -156,6 +156,7 @@ int main(int argc, char **argv)
/* trace file */ /* trace file */
tracefile = optarg; tracefile = optarg;
break; break;
/* trace options */
case 'T': case 'T':
sieve_tool_parse_trace_option(&tr_config, optarg); sieve_tool_parse_trace_option(&tr_config, optarg);
break; break;
...@@ -172,13 +173,16 @@ int main(int argc, char **argv) ...@@ -172,13 +173,16 @@ int main(int argc, char **argv)
array_append(&scriptfiles, &file, 1); array_append(&scriptfiles, &file, 1);
} }
break; break;
/* execution mode */
case 'e': case 'e':
execute = TRUE; execute = TRUE;
break; break;
/* force script compile */
case 'C': case 'C':
force_compile = TRUE; force_compile = TRUE;
break; break;
default: default:
/* unrecognized option */
print_help(); print_help();
i_fatal_status(EX_USAGE, "Unknown argument: %c", c); i_fatal_status(EX_USAGE, "Unknown argument: %c", c);
break; break;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment

Consent

On this website, we use the web analytics service Matomo to analyze and review the use of our website. Through the collected statistics, we can improve our offerings and make them more appealing for you. Here, you can decide whether to allow us to process your data and set corresponding cookies for these purposes, in addition to technically necessary cookies. Further information on data protection—especially regarding "cookies" and "Matomo"—can be found in our privacy policy. You can withdraw your consent at any time.