37
37
"Usage: sieve-filter [-c <config-file>] [-C] [-D] [-e] [-m <default-mailbox>]\n"
38
38
" [-P <plugin>] [-q <output-mailbox>] [-Q <mail-command>]\n"
39
" [-s <script-file>] [-u <user>] [-W] [-x <extensions>]\n"
40
" <script-file> <source-mailbox> <source-action>\n"
39
" [-s <script-file>] [-u <user>] [-v] [-W] [-x <extensions>]\n"
40
" <script-file> <source-mailbox> [<discard-action>]\n"
44
enum sieve_filter_source_action {
45
SIEVE_FILTER_SACT_KEEP, /* Always keep messages in source folder */
46
SIEVE_FILTER_SACT_MOVE, /* Move discarded messages to Trash folder */
47
SIEVE_FILTER_SACT_DELETE, /* Flag discarded messages as \DELETED */
48
SIEVE_FILTER_SACT_EXPUNGE /* Expunge discarded messages */
44
enum sieve_filter_discard_action {
45
SIEVE_FILTER_DACT_KEEP, /* Keep discarded messages in source folder */
46
SIEVE_FILTER_DACT_MOVE, /* Move discarded messages to Trash folder */
47
SIEVE_FILTER_DACT_DELETE, /* Flag discarded messages as \DELETED */
48
SIEVE_FILTER_DACT_EXPUNGE /* Expunge discarded messages */
51
51
struct sieve_filter_data {
52
enum sieve_filter_source_action source_action;
52
enum sieve_filter_discard_action discard_action;
53
53
struct mailbox *move_mailbox;
55
55
struct sieve_script_env *senv;
77
76
struct sieve_exec_status estatus;
78
77
struct sieve_binary *sbin;
79
78
struct sieve_message_data msgdata;
80
const char *recipient, *sender;
79
const char *recipient = NULL, *sender = NULL;
81
80
bool execute = sfctx->data->execute;
82
81
bool source_write = sfctx->data->source_write;
83
bool move_mode = sfctx->data->move_mode;
82
const char *subject, *date;
86
86
sieve_tool_get_envelope_data(mail, &recipient, &sender);
97
97
msgdata.final_envelope_to = recipient;
98
98
msgdata.auth_user = senv->username;
99
99
(void)mail_get_first_header(mail, "Message-ID", &msgdata.id);
101
if ( mail_get_virtual_size(mail, &size) < 0 ) {
102
if ( mail->expunged )
105
sieve_error(ehandler, NULL, "failed to obtain message size; "
106
"skipping this message (id=%s)",
107
( msgdata.id == NULL ? "none" : msgdata.id ));
111
if ( mail_get_first_header(mail, "date", &date) <= 0 )
113
if ( mail_get_first_header(mail, "subject", &subject) <= 0 )
101
116
/* Single script */
102
117
sbin = sfctx->data->main_sbin;
104
119
/* Execute script */
106
ret = sieve_execute(sbin, &msgdata, senv, ehandler, NULL);
121
sieve_info(ehandler, NULL,
122
"filtering: [%s; %"PRIuUOFF_T" bytes] `%s'", date, size,
123
str_sanitize(subject, 40));
125
ret = sieve_execute(sbin, &msgdata, senv, ehandler, 0, NULL);
127
(void)o_stream_send_str(sfctx->teststream,
128
t_strdup_printf(">> Filtering message:\n\n"
131
" Size: %"PRIuUOFF_T" bytes\n"
132
" Subject: %s\n", ( msgdata.id == NULL ? "none" : msgdata.id ),
133
date, size, str_sanitize(subject, 40)));
109
(sbin, &msgdata, senv, ehandler, sfctx->teststream, NULL);
136
(sbin, &msgdata, senv, ehandler, sfctx->teststream, 0, NULL);
112
139
/* Handle message in source folder */
114
141
struct mailbox *move_box = sfctx->data->move_mailbox;
115
enum sieve_filter_source_action source_action =
116
sfctx->data->source_action;
142
enum sieve_filter_discard_action discard_action =
143
sfctx->data->discard_action;
118
145
if ( !source_write ) {
146
/* READ-ONLY; Do nothing */
121
148
} else if ( estatus.keep_original ) {
149
/* Explicitly `stored' in source box; just keep it there */
122
150
sieve_info(ehandler, NULL, "message kept in source mailbox");
124
} else if ( move_mode && estatus.message_saved ) {
152
} else if ( estatus.message_saved ) {
125
153
sieve_info(ehandler, NULL,
126
154
"message expunged from source mailbox upon successful move");
129
157
mail_expunge(mail);
132
switch ( source_action ) {
161
switch ( discard_action ) {
133
162
/* Leave it there */
134
case SIEVE_FILTER_SACT_KEEP:
163
case SIEVE_FILTER_DACT_KEEP:
135
164
sieve_info(ehandler, NULL, "message left in source mailbox");
137
166
/* Move message to indicated folder */
138
case SIEVE_FILTER_SACT_MOVE:
167
case SIEVE_FILTER_DACT_MOVE:
139
168
sieve_info(ehandler, NULL,
140
169
"message in source mailbox moved to mailbox '%s'",
141
170
mailbox_get_name(move_box));
165
194
/* Flag message as \DELETED */
166
case SIEVE_FILTER_SACT_DELETE:
195
case SIEVE_FILTER_DACT_DELETE:
167
196
sieve_info(ehandler, NULL, "message flagged as deleted in source mailbox");
169
198
mail_update_flags(mail, MODIFY_ADD, MAIL_DELETED);
171
200
/* Expunge the message immediately */
172
case SIEVE_FILTER_SACT_EXPUNGE:
201
case SIEVE_FILTER_DACT_EXPUNGE:
173
202
sieve_info(ehandler, NULL, "message expunged from source mailbox");
175
204
mail_expunge(mail);
217
case SIEVE_EXEC_BIN_CORRUPT:
218
sieve_error(ehandler, NULL, "sieve script binary is corrupt");
220
case SIEVE_EXEC_FAILURE:
221
case SIEVE_EXEC_KEEP_FAILED:
222
sieve_error(ehandler, NULL,
223
"sieve script execution failed for this message; "
224
"message left in source mailbox");
227
sieve_error(ehandler, NULL,
228
"sieve execution result: unrecognized return value?!");
188
235
/* FIXME: introduce this into Dovecot */
241
288
mail_search_build_add_flags(search_args, MAIL_DELETED, TRUE);
243
290
t = mailbox_transaction_begin(src_box, 0);
244
search_ctx = mailbox_search_init(t, search_args, NULL);
291
search_ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
245
292
mail_search_args_unref(&search_args);
247
294
/* Iterate through all requested messages */
249
mail = mail_alloc(t, 0, NULL);
250
while ( ret > 0 && mailbox_search_next(search_ctx, mail) > 0 ) {
251
const char *subject, *date;
254
/* Request message size */
256
if ( mail_get_virtual_size(mail, &size) < 0 ) {
257
if ( mail->expunged )
260
sieve_error(ehandler, NULL, "failed to obtain message size");
264
if ( mail_get_first_header(mail, "date", &date) <= 0 )
266
if ( mail_get_first_header(mail, "subject", &subject) <= 0 )
269
sieve_info(ehandler, NULL,
270
"filtering: [%s; %"PRIuUOFF_T" bytes] %s", date, size,
271
str_sanitize(subject, 40));
296
while ( ret >= 0 && mailbox_search_next(search_ctx, &mail) > 0 ) {
273
297
ret = filter_message(&sfctx, mail);
283
306
if ( sfctx.move_trans != NULL ) {
285
mailbox_transaction_rollback(&sfctx.move_trans);
287
if ( mailbox_transaction_commit(&sfctx.move_trans) < 0 ) {
294
mailbox_transaction_rollback(&t);
296
if ( mailbox_transaction_commit(&t) < 0 ) {
307
if ( mailbox_transaction_commit(&sfctx.move_trans) < 0 ) {
312
if ( mailbox_transaction_commit(&t) < 0 ) {
301
316
if ( sfctx.teststream != NULL )
302
317
o_stream_destroy(&sfctx.teststream);
335
350
ARRAY_TYPE (const_string) scriptfiles;
336
351
const char *scriptfile, *src_mailbox, *dst_mailbox, *move_mailbox;
337
352
struct sieve_filter_data sfdata;
338
enum sieve_filter_source_action source_action = SIEVE_FILTER_SACT_KEEP;
353
enum sieve_filter_discard_action discard_action = SIEVE_FILTER_DACT_KEEP;
339
354
struct mail_user *mail_user;
340
355
struct sieve_binary *main_sbin;
341
356
struct sieve_script_env scriptenv;
342
357
struct sieve_error_handler *ehandler;
343
bool force_compile, execute, source_write, move_mode;
358
bool force_compile, execute, source_write, verbose;
344
359
struct mail_namespace *ns;
345
360
struct mailbox *src_box = NULL, *move_box = NULL;
346
enum mailbox_flags open_flags =
347
MAILBOX_FLAG_KEEP_RECENT | MAILBOX_FLAG_IGNORE_ACLS;
361
enum mailbox_flags open_flags = MAILBOX_FLAG_IGNORE_ACLS;
348
362
enum mail_error error;
351
365
sieve_tool = sieve_tool_init("sieve-filter", &argc, &argv,
352
"m:s:x:P:u:q:Q:DCeWM", FALSE);
366
"m:s:x:P:u:q:Q:DCevW", FALSE);
354
368
t_array_init(&scriptfiles, 16);
356
370
/* Parse arguments */
357
371
scriptfile = NULL;
358
372
src_mailbox = dst_mailbox = move_mailbox = NULL;
359
force_compile = execute = source_write = move_mode = FALSE;
373
force_compile = execute = source_write = verbose = FALSE;
360
374
while ((c = sieve_tool_getopt(sieve_tool)) > 0) {
429
443
const char *srcact = argv[optind++];
431
445
if ( strcmp(srcact, "keep") == 0 ) {
432
source_action = SIEVE_FILTER_SACT_KEEP;
446
discard_action = SIEVE_FILTER_DACT_KEEP;
433
447
} else if ( strcmp(srcact, "move") == 0 ) {
434
source_action = SIEVE_FILTER_SACT_MOVE;
448
discard_action = SIEVE_FILTER_DACT_MOVE;
435
449
if ( optind < argc ) {
436
450
move_mailbox = t_strdup(argv[optind++]);
439
453
i_fatal_status(EX_USAGE,
440
"Invalid <source-action> argument: "
454
"Invalid <discard-action> argument: "
441
455
"the `move' action requires mailbox argument");
443
} else if ( strcmp(srcact, "flag") == 0 ) {
444
source_action = SIEVE_FILTER_SACT_DELETE;
457
} else if ( strcmp(srcact, "delete") == 0 ) {
458
discard_action = SIEVE_FILTER_DACT_DELETE;
445
459
} else if ( strcmp(srcact, "expunge") == 0 ) {
446
source_action = SIEVE_FILTER_SACT_EXPUNGE;
460
discard_action = SIEVE_FILTER_DACT_EXPUNGE;
449
i_fatal_status(EX_USAGE, "Invalid <source-action> argument");
463
i_fatal_status(EX_USAGE, "Invalid <discard-action> argument");
462
476
/* Finish tool initialization */
463
477
svinst = sieve_tool_init_finish(sieve_tool, TRUE);
465
/* Enable debug extension */
466
sieve_enable_debug_extension(svinst);
479
/* Enable debug extension */
480
sieve_enable_debug_extension(svinst);
468
482
/* Create error handler */
469
483
ehandler = sieve_stderr_ehandler_create(svinst, 0);
470
484
sieve_system_ehandler_set(ehandler);
471
sieve_error_handler_accept_infolog(ehandler, TRUE);
485
sieve_error_handler_accept_infolog(ehandler, verbose);
473
487
/* Compile main sieve script */
474
488
if ( force_compile ) {
501
515
/* Open move box if necessary */
503
if ( execute && source_action == SIEVE_FILTER_SACT_MOVE &&
517
if ( execute && discard_action == SIEVE_FILTER_DACT_MOVE &&
504
518
move_mailbox != NULL ) {
505
519
move_mailbox = mailbox_name_to_mutf7(move_mailbox);
506
ns = mail_namespace_find(mail_user->namespaces, &move_mailbox);
520
ns = mail_namespace_find(mail_user->namespaces, move_mailbox);
507
521
if ( ns == NULL )
508
522
i_fatal("Unknown namespace for mailbox '%s'", move_mailbox);
532
546
/* Compose filter context */
533
547
memset(&sfdata, 0, sizeof(sfdata));
534
548
sfdata.senv = &scriptenv;
535
sfdata.source_action = source_action;
549
sfdata.discard_action = discard_action;
536
550
sfdata.move_mailbox = move_box;
537
551
sfdata.main_sbin = main_sbin;
538
552
sfdata.ehandler = ehandler;
539
553
sfdata.execute = execute;
540
554
sfdata.source_write = source_write;
541
sfdata.move_mode = move_mode;
543
556
/* Apply Sieve filter to all messages found */
544
557
(void) filter_mailbox(&sfdata, src_box);