diff options
Diffstat (limited to 'pigeonhole/src/sieve-tools/sieve-test.c')
-rw-r--r-- | pigeonhole/src/sieve-tools/sieve-test.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/pigeonhole/src/sieve-tools/sieve-test.c b/pigeonhole/src/sieve-tools/sieve-test.c new file mode 100644 index 0000000..e7345be --- /dev/null +++ b/pigeonhole/src/sieve-tools/sieve-test.c @@ -0,0 +1,515 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "env-util.h" +#include "str.h" +#include "ostream.h" +#include "array.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "master-service.h" +#include "master-service-settings.h" +#include "mail-storage-service.h" + +#include "sieve.h" +#include "sieve-binary.h" +#include "sieve-extensions.h" + +#include "sieve-tool.h" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <pwd.h> +#include <sysexits.h> + + +/* + * Configuration + */ + +#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail" + +/* + * Print help + */ + +static void print_help(void) +{ + printf( +"Usage: sieve-test [-a <orig-recipient-address] [-c <config-file>]\n" +" [-C] [-D] [-d <dump-filename>] [-e]\n" +" [-f <envelope-sender>] [-l <mail-location>]\n" +" [-m <default-mailbox>] [-P <plugin>]\n" +" [-r <recipient-address>] [-s <script-file>]\n" +" [-t <trace-file>] [-T <trace-option>] [-x <extensions>]\n" +" <script-file> <mail-file>\n" + ); +} + +/* + * Dummy SMTP session + */ + +static void * +sieve_smtp_start(const struct sieve_script_env *senv ATTR_UNUSED, + const struct smtp_address *mail_from) +{ + struct ostream *output; + + i_info("sending message from <%s>:", smtp_address_encode(mail_from)); + + output = o_stream_create_fd(STDOUT_FILENO, (size_t)-1); + o_stream_set_no_error_handling(output, TRUE); + return (void*)output; +} + +static void +sieve_smtp_add_rcpt(const struct sieve_script_env *senv ATTR_UNUSED, + void *handle ATTR_UNUSED, + const struct smtp_address *rcpt_to) +{ + printf("\nRECIPIENT: %s\n", smtp_address_encode(rcpt_to)); +} + +static struct ostream * +sieve_smtp_send(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) +{ + printf("START MESSAGE:\n"); + + return (struct ostream *)handle; +} + +static void +sieve_smtp_abort(const struct sieve_script_env *senv ATTR_UNUSED, void *handle) +{ + struct ostream *output = (struct ostream *)handle; + + printf("#### ABORT MESSAGE ####\n\n"); + o_stream_unref(&output); +} + +static int +sieve_smtp_finish(const struct sieve_script_env *senv ATTR_UNUSED, void *handle, + const char **error_r ATTR_UNUSED) +{ + struct ostream *output = (struct ostream *)handle; + + printf("END MESSAGE\n\n"); + o_stream_unref(&output); + return 1; +} + +/* + * Dummy duplicate check implementation + */ + +static void * +duplicate_transaction_begin(const struct sieve_script_env *senv ATTR_UNUSED) +{ + return NULL; +} + +static void duplicate_transaction_commit(void **_dup_trans ATTR_UNUSED) +{ +} + +static void duplicate_transaction_rollback(void **_dup_trans ATTR_UNUSED) +{ +} + +static int +duplicate_check(void *_dup_trans ATTR_UNUSED, + const struct sieve_script_env *senv, + const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED) +{ + i_info("checked duplicate for user %s.\n", senv->user->username); + return 0; +} + +static void +duplicate_mark(void *_dup_trans ATTR_UNUSED, + const struct sieve_script_env *senv, + const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, + time_t time ATTR_UNUSED) +{ + i_info("marked duplicate for user %s.\n", senv->user->username); +} + +/* + * Result logging + */ + +static const char * +result_amend_log_message(const struct sieve_script_env *senv, + enum log_type log_type, const char *message) +{ + const struct sieve_message_data *msgdata = senv->script_context; + string_t *str; + + if (log_type == LOG_TYPE_DEBUG) + return message; + + str = t_str_new(256); + str_printfa(str, "msgid=%s", (msgdata->id == NULL ? + "unspecified" : msgdata->id)); + str_append(str, ": "); + str_append(str, message); + + return str_c(str); +} + +/* + * Tool implementation + */ + +int main(int argc, char **argv) +{ + struct sieve_instance *svinst; + ARRAY_TYPE (const_string) scriptfiles; + const char *scriptfile, *mailbox, *dumpfile, *tracefile, *mailfile, + *mailloc, *errstr; + struct smtp_address *rcpt_to, *final_rcpt_to, *mail_from; + struct sieve_trace_config trace_config; + struct mail *mail; + struct sieve_binary *main_sbin, *sbin = NULL; + struct sieve_message_data msgdata; + struct sieve_script_env scriptenv; + struct sieve_exec_status estatus; + enum sieve_execute_flags exflags = SIEVE_EXECUTE_FLAG_LOG_RESULT; + struct sieve_error_handler *ehandler; + struct ostream *teststream = NULL; + struct sieve_trace_log *trace_log = NULL; + bool force_compile = FALSE, execute = FALSE; + int exit_status = EXIT_SUCCESS; + int ret, c; + + sieve_tool = sieve_tool_init("sieve-test", &argc, &argv, + "r:a:f:m:d:l:s:eCt:T:DP:x:u:", FALSE); + + ehandler = NULL; + t_array_init(&scriptfiles, 16); + + /* Parse arguments */ + mailbox = dumpfile = tracefile = mailloc = NULL; + mail_from = final_rcpt_to = rcpt_to = NULL; + i_zero(&trace_config); + trace_config.level = SIEVE_TRLVL_ACTIONS; + while ((c = sieve_tool_getopt(sieve_tool)) > 0) { + switch (c) { + case 'r': + /* final recipient address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART, + &final_rcpt_to, &errstr) < 0) + i_fatal("Invalid -r parameter: %s", errstr); + break; + case 'a': + /* original recipient address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART, + &rcpt_to, &errstr) < 0) + i_fatal("Invalid -a parameter: %s", errstr); + break; + case 'f': + /* envelope sender address */ + if (smtp_address_parse_mailbox( + pool_datastack_create(), optarg, + 0, &mail_from, &errstr) < 0) + i_fatal("Invalid -f parameter: %s", errstr); + break; + case 'm': + /* default mailbox (keep box) */ + mailbox = optarg; + break; + case 'l': + /* mail location */ + mailloc = optarg; + break; + case 't': + /* trace file */ + tracefile = optarg; + break; + /* trace options */ + case 'T': + sieve_tool_parse_trace_option(&trace_config, optarg); + break; + case 'd': + /* dump file */ + dumpfile = optarg; + break; + case 's': + /* scriptfile executed before main script */ + { + const char *file; + + file = t_strdup(optarg); + array_append(&scriptfiles, &file, 1); + } + break; + /* execution mode */ + case 'e': + execute = TRUE; + break; + /* force script compile */ + case 'C': + force_compile = TRUE; + break; + default: + /* unrecognized option */ + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %c", c); + break; + } + } + + if (optind < argc) + scriptfile = argv[optind++]; + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <script-file> argument"); + } + + if (optind < argc) + mailfile = argv[optind++]; + else { + print_help(); + i_fatal_status(EX_USAGE, "Missing <mail-file> argument"); + } + + if (optind != argc) { + print_help(); + i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]); + } + + /* Finish tool initialization */ + svinst = sieve_tool_init_finish(sieve_tool, mailloc == NULL, FALSE); + + /* Enable debug extension */ + sieve_enable_debug_extension(svinst); + + /* Create error handler */ + ehandler = sieve_stderr_ehandler_create(svinst, 0); + sieve_error_handler_accept_infolog(ehandler, TRUE); + sieve_error_handler_accept_debuglog(ehandler, svinst->debug); + + /* Compile main sieve script */ + if (force_compile) { + main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL); + if (main_sbin != NULL) + (void)sieve_save(main_sbin, TRUE, NULL); + } else { + main_sbin = sieve_tool_script_open(svinst, scriptfile); + } + + if (main_sbin == NULL) { + exit_status = EXIT_FAILURE; + } else { + /* Dump script */ + sieve_tool_dump_binary_to(main_sbin, dumpfile, FALSE); + + /* Obtain mail namespaces from -l argument */ + if (mailloc != NULL) + sieve_tool_init_mail_user(sieve_tool, mailloc); + + /* Initialize raw mail object */ + mail = sieve_tool_open_file_as_mail(sieve_tool, mailfile); + + if (mailbox == NULL) + mailbox = "INBOX"; + + /* Collect necessary message data */ + i_zero(&msgdata); + msgdata.mail = mail; + msgdata.auth_user = sieve_tool_get_username(sieve_tool); + (void)mail_get_message_id(mail, &msgdata.id); + + sieve_tool_get_envelope_data(&msgdata, mail, + mail_from, rcpt_to, final_rcpt_to); + + /* Create streams for test and trace output */ + + if (!execute) { + teststream = o_stream_create_fd(1, 0); + o_stream_set_no_error_handling(teststream, TRUE); + } + + if (tracefile != NULL) { + (void)sieve_trace_log_create( + svinst, (strcmp(tracefile, "-") == 0 ? + NULL : tracefile), &trace_log); + } + + /* Compose script environment */ + if (sieve_script_env_init( + &scriptenv, sieve_tool_get_mail_user(sieve_tool), + &errstr) < 0) { + i_fatal("Failed to initialize script execution: %s", + errstr); + } + + scriptenv.default_mailbox = mailbox; + scriptenv.smtp_start = sieve_smtp_start; + scriptenv.smtp_add_rcpt = sieve_smtp_add_rcpt; + scriptenv.smtp_send = sieve_smtp_send; + scriptenv.smtp_abort = sieve_smtp_abort; + scriptenv.smtp_finish = sieve_smtp_finish; + scriptenv.duplicate_transaction_begin = + duplicate_transaction_begin; + scriptenv.duplicate_transaction_commit = + duplicate_transaction_commit; + scriptenv.duplicate_transaction_rollback = + duplicate_transaction_rollback; + scriptenv.duplicate_mark = duplicate_mark; + scriptenv.duplicate_check = duplicate_check; + scriptenv.result_amend_log_message = result_amend_log_message; + scriptenv.trace_log = trace_log; + scriptenv.trace_config = trace_config; + scriptenv.script_context = &msgdata; + + i_zero(&estatus); + scriptenv.exec_status = &estatus; + + /* Run the test */ + ret = 1; + if (array_count(&scriptfiles) == 0) { + /* Single script */ + sbin = main_sbin; + main_sbin = NULL; + + /* Execute/Test script */ + if (execute) { + ret = sieve_execute(sbin, &msgdata, &scriptenv, + ehandler, ehandler, + exflags); + } else { + ret = sieve_test(sbin, &msgdata, &scriptenv, + ehandler, teststream, exflags); + } + } else { + /* Multiple scripts */ + const char *const *sfiles; + unsigned int i, count; + struct sieve_multiscript *mscript; + bool more = TRUE; + + if (execute) + mscript = sieve_multiscript_start_execute( + svinst, &msgdata, &scriptenv); + else + mscript = sieve_multiscript_start_test( + svinst, &msgdata, &scriptenv, + teststream); + + /* Execute scripts sequentially */ + sfiles = array_get(&scriptfiles, &count); + for (i = 0; i < count && more; i++) { + if (teststream != NULL) { + o_stream_nsend_str( + teststream, + t_strdup_printf("\n## Executing script: %s\n", + sfiles[i])); + } + + /* Close previous script */ + if (sbin != NULL) + sieve_close(&sbin); + + /* Compile sieve script */ + if (force_compile) { + sbin = sieve_tool_script_compile( + svinst, sfiles[i], sfiles[i]); + if (sbin != NULL) + (void)sieve_save(sbin, FALSE, NULL); + } else { + sbin = sieve_tool_script_open(svinst, sfiles[i]); + } + + if (sbin == NULL) { + ret = SIEVE_EXEC_FAILURE; + break; + } + + /* Execute/Test script */ + more = sieve_multiscript_run( + mscript, sbin, ehandler, ehandler, + exflags); + } + + /* Execute/Test main script */ + if (more && ret > 0) { + if (teststream != NULL) { + o_stream_nsend_str( + teststream, + t_strdup_printf("## Executing script: %s\n", + scriptfile)); + } + + /* Close previous script */ + if (sbin != NULL) + sieve_close(&sbin); + + sbin = main_sbin; + main_sbin = NULL; + + (void)sieve_multiscript_run( + mscript, sbin, ehandler, ehandler, + exflags); + } + + ret = sieve_multiscript_finish(&mscript, ehandler, + exflags, ret); + } + + /* Run */ + switch (ret) { + case SIEVE_EXEC_OK: + i_info("final result: success"); + break; + case SIEVE_EXEC_RESOURCE_LIMIT: + i_info("resource limit exceeded"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_BIN_CORRUPT: + i_info("corrupt binary deleted."); + i_unlink_if_exists(sieve_binary_path(sbin)); + /* fall through */ + case SIEVE_EXEC_FAILURE: + i_info("final result: failed; " + "resolved with successful implicit keep"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_TEMP_FAILURE: + i_info("final result: temporary failure"); + exit_status = EXIT_FAILURE; + break; + case SIEVE_EXEC_KEEP_FAILED: + i_info("final result: utter failure"); + exit_status = EXIT_FAILURE; + break; + } + + if (teststream != NULL) + o_stream_destroy(&teststream); + if (trace_log != NULL) + sieve_trace_log_free(&trace_log); + + /* Cleanup remaining binaries */ + if (sbin != NULL) + sieve_close(&sbin); + if (main_sbin != NULL) + sieve_close(&main_sbin); + } + + /* Cleanup error handler */ + sieve_error_handler_unref(&ehandler); + + sieve_tool_deinit(&sieve_tool); + + return exit_status; +} |