summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/sieve-tools/sieve-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/sieve-tools/sieve-test.c')
-rw-r--r--pigeonhole/src/sieve-tools/sieve-test.c515
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;
+}