From: Stephan Bosch Date: Fri, 5 May 2017 15:06:32 +0000 (+0200) Subject: lib-smtp: smtp-submit: Added support for asynchronous message submission. X-Git-Tag: 2.3.0.rc1~1046 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=faca780e2331e81bba56d484ae2b6e758d3ac3f3;p=thirdparty%2Fdovecot%2Fcore.git lib-smtp: smtp-submit: Added support for asynchronous message submission. --- diff --git a/src/lib-lda/mail-send.c b/src/lib-lda/mail-send.c index 5527afcefc..371687b9cb 100644 --- a/src/lib-lda/mail-send.c +++ b/src/lib-lda/mail-send.c @@ -87,7 +87,7 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, smtp_set.submission_host = ctx->set->submission_host; smtp_set.sendmail_path = ctx->set->sendmail_path; - smtp_submit = smtp_submit_init(&smtp_set, NULL); + smtp_submit = smtp_submit_init_simple(&smtp_set, NULL); smtp_submit_add_rcpt(smtp_submit, return_addr); output = smtp_submit_send(smtp_submit); @@ -187,7 +187,7 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, str_truncate(str, 0); str_printfa(str, "\r\n\r\n--%s--\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); - if ((ret = smtp_submit_deinit_timeout + if ((ret = smtp_submit_run_timeout (smtp_submit, ctx->timeout_secs, &error)) < 0) { i_error("msgid=%s: Temporarily failed to send rejection: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), @@ -197,5 +197,6 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(error, 512)); } + smtp_submit_deinit(&smtp_submit); return ret < 0 ? -1 : 0; } diff --git a/src/lib-lda/smtp-client.c b/src/lib-lda/smtp-client.c index 6f8d8d7a5f..c8702a60c9 100644 --- a/src/lib-lda/smtp-client.c +++ b/src/lib-lda/smtp-client.c @@ -21,7 +21,7 @@ smtp_client_init(const struct lda_settings *set, const char *return_path) smtp_set.sendmail_path = set->sendmail_path; client = i_new(struct smtp_client, 1); - client->submit = smtp_submit_init(&smtp_set, return_path); + client->submit = smtp_submit_init_simple(&smtp_set, return_path); return client; } @@ -41,7 +41,7 @@ void smtp_client_abort(struct smtp_client **_client) *_client = NULL; - smtp_submit_abort(&client->submit); + smtp_submit_deinit(&client->submit); i_free(client); } @@ -53,9 +53,11 @@ int smtp_client_deinit(struct smtp_client *client, const char **error_r) int smtp_client_deinit_timeout(struct smtp_client *client, unsigned int timeout_secs, const char **error_r) { - int ret; + int ret; - ret = smtp_submit_deinit_timeout(client->submit, timeout_secs, error_r); + ret = smtp_submit_run_timeout(client->submit, timeout_secs, error_r); + smtp_submit_deinit(&client->submit); i_free(client); + return ret; } diff --git a/src/lib-smtp/smtp-submit.c b/src/lib-smtp/smtp-submit.c index 749c8f0831..4da0086e3f 100644 --- a/src/lib-smtp/smtp-submit.c +++ b/src/lib-smtp/smtp-submit.c @@ -19,40 +19,115 @@ #define DEFAULT_SUBMISSION_PORT 25 +struct smtp_submit_session { + pool_t pool; + struct smtp_submit_settings set; +}; + struct smtp_submit { pool_t pool; + + struct smtp_submit_session *session; + struct ostream *output; struct istream *input; - struct smtp_submit_settings set; ARRAY_TYPE(const_string) destinations; const char *return_path; + + struct timeout *to_error; + int status; const char *error; - bool success:1; - bool finished:1; - bool tempfail:1; + struct program_client *prg_client; + struct lmtp_client *lmtp_client; + + smtp_submit_callback_t *callback; + void *context; + + bool simple:1; }; +struct smtp_submit_session * +smtp_submit_session_init(const struct smtp_submit_settings *set) +{ + struct smtp_submit_session *session; + pool_t pool; + + pool = pool_alloconly_create("smtp submit session", 128); + session = p_new(pool, struct smtp_submit_session, 1); + session->pool = pool; + + session->set.hostname = p_strdup_empty(pool, set->hostname); + session->set.submission_host = p_strdup_empty(pool, set->submission_host); + session->set.sendmail_path = p_strdup_empty(pool, set->sendmail_path); + return session; +} + +void smtp_submit_session_deinit(struct smtp_submit_session **_session) +{ + struct smtp_submit_session *session = *_session; + + *_session = NULL; + + pool_unref(&session->pool); +} + struct smtp_submit * -smtp_submit_init(const struct smtp_submit_settings *set, const char *return_path) +smtp_submit_init(struct smtp_submit_session *session, const char *return_path) { struct smtp_submit *subm; pool_t pool; pool = pool_alloconly_create("smtp submit", 256); subm = p_new(pool, struct smtp_submit, 1); + subm->session = session; subm->pool = pool; - subm->set.hostname = p_strdup_empty(pool, set->hostname); - subm->set.submission_host = p_strdup_empty(pool, set->submission_host); - subm->set.sendmail_path = p_strdup_empty(pool, set->sendmail_path); - subm->return_path = p_strdup(pool, return_path); p_array_init(&subm->destinations, pool, 2); return subm; } +struct smtp_submit * +smtp_submit_init_simple(const struct smtp_submit_settings *set, + const char *return_path) +{ + struct smtp_submit_session *session; + struct smtp_submit *subm; + + session = smtp_submit_session_init(set); + subm = smtp_submit_init(session, return_path); + subm->simple = TRUE; + return subm; +} + +void smtp_submit_deinit(struct smtp_submit **_subm) +{ + struct smtp_submit *subm = *_subm; + + *_subm = NULL; + + if (subm->output != NULL) { + o_stream_ignore_last_errors(subm->output); + o_stream_destroy(&subm->output); + } + if (subm->input != NULL) + i_stream_destroy(&subm->input); + + if (subm->prg_client != NULL) + program_client_destroy(&subm->prg_client); + if (subm->lmtp_client != NULL) + lmtp_client_deinit(&subm->lmtp_client); + + if (subm->to_error != NULL) + timeout_remove(&subm->to_error); + + if (subm->simple) + smtp_submit_session_deinit(&subm->session); + pool_unref(&subm->pool); +} + void smtp_submit_add_rcpt(struct smtp_submit *subm, const char *address) { i_assert(subm->output == NULL); @@ -73,86 +148,132 @@ struct ostream *smtp_submit_send(struct smtp_submit *subm) return subm->output; } -static void smtp_submit_send_finished(void *context) +static void +smtp_submit_callback(struct smtp_submit *subm, int status, + const char *error) { - struct smtp_submit *smtp_submit = context; + struct smtp_submit_result result; + smtp_submit_callback_t *callback; - smtp_submit->finished = TRUE; - io_loop_stop(current_ioloop); + if (subm->to_error != NULL) + timeout_remove(&subm->to_error); + + i_zero(&result); + result.status = status; + result.error = error; + + callback = subm->callback; + subm->callback = NULL; + callback(&result, subm->context); +} + +static void +smtp_submit_delayed_error_callback(struct smtp_submit *subm) +{ + smtp_submit_callback(subm, -1, subm->error); +} + +static void +smtp_submit_delayed_error(struct smtp_submit *subm, + const char *error) +{ + subm->status = -1; + subm->error = p_strdup(subm->pool, error); + subm->to_error = timeout_add_short(0, + smtp_submit_delayed_error_callback, subm); } static void smtp_submit_error(struct smtp_submit *subm, - bool tempfail, const char *error) + int status, const char *error) { - if (subm->error == NULL) { - subm->tempfail = tempfail; - subm->error = p_strdup_printf(subm->pool, - "smtp(%s): %s", - subm->set.submission_host, error); - } + const struct smtp_submit_settings *set = &subm->session->set; + i_assert(status <= 0); + if (subm->error != NULL) + return; + + subm->status = status; + subm->error = p_strdup_printf(subm->pool, + "smtp(%s): %s", + set->submission_host, error); +} + +static void +smtp_submit_success(struct smtp_submit *subm) +{ + if (subm->error != NULL) + return; + subm->status = 1; +} + +static void +smtp_submit_send_host_finished(void *context) +{ + struct smtp_submit *subm = (struct smtp_submit *)context; + + i_assert(subm->status > 0 || subm->error != NULL); + smtp_submit_callback(subm, subm->status, subm->error); } static void rcpt_to_callback(enum lmtp_client_result result, const char *reply, void *context) { - struct smtp_submit *subm = context; + struct smtp_submit *subm = (struct smtp_submit *)context; if (result != LMTP_CLIENT_RESULT_OK) { - smtp_submit_error(subm, (reply[0] != '5'), + smtp_submit_error(subm, + (reply[0] != '5' ? -1 : 0), t_strdup_printf("RCPT TO failed: %s", reply)); - smtp_submit_send_finished(subm); } } static void data_callback(enum lmtp_client_result result, const char *reply, void *context) { - struct smtp_submit *subm = context; + struct smtp_submit *subm = (struct smtp_submit *)context; if (result != LMTP_CLIENT_RESULT_OK) { - smtp_submit_error(subm, (reply[0] != '5'), + smtp_submit_error(subm, + (reply[0] != '5' ? -1 : 0), t_strdup_printf("DATA failed: %s", reply)); - smtp_submit_send_finished(subm); - } else { - subm->success = TRUE; + return; } + + smtp_submit_success(subm); } -static int +static void smtp_submit_send_host(struct smtp_submit *subm, - unsigned int timeout_secs, const char **error_r) + unsigned int timeout_secs) { + const struct smtp_submit_settings *set = &subm->session->set; struct lmtp_client_settings client_set; struct lmtp_client *lmtp_client; - struct ioloop *ioloop; const char *host, *const *destp; in_port_t port; - if (net_str2hostport(subm->set.submission_host, - DEFAULT_SUBMISSION_PORT, &host, &port) < 0) { - *error_r = t_strdup_printf( - "Invalid submission_host: %s", host); - return -1; + if (net_str2hostport(set->submission_host, + DEFAULT_SUBMISSION_PORT, &host, &port) < 0) { + smtp_submit_delayed_error(subm, t_strdup_printf( + "Invalid submission_host: %s", host)); + return; } i_zero(&client_set); client_set.mail_from = subm->return_path == NULL ? "<>" : t_strconcat("<", subm->return_path, ">", NULL); - client_set.my_hostname = subm->set.hostname; + client_set.my_hostname = set->hostname; client_set.timeout_secs = timeout_secs; - ioloop = io_loop_create(); - lmtp_client = lmtp_client_init(&client_set, smtp_submit_send_finished, - subm); + lmtp_client = lmtp_client_init(&client_set, + smtp_submit_send_host_finished, subm); if (lmtp_client_connect_tcp(lmtp_client, LMTP_CLIENT_PROTOCOL_SMTP, host, port) < 0) { + smtp_submit_delayed_error(subm, t_strdup_printf( + "Couldn't connect to %s:%u", host, port)); lmtp_client_deinit(&lmtp_client); - io_loop_destroy(&ioloop); - *error_r = t_strdup_printf("Couldn't connect to %s:%u", - host, port); - return -1; + return; } array_foreach(&subm->destinations, destp) { @@ -160,39 +281,41 @@ smtp_submit_send_host(struct smtp_submit *subm, data_callback, subm); } + subm->lmtp_client = lmtp_client; + lmtp_client_send(lmtp_client, subm->input); i_stream_unref(&subm->input); +} - if (!subm->finished) - io_loop_run(ioloop); - lmtp_client_deinit(&lmtp_client); - io_loop_destroy(&ioloop); - - if (subm->success) - return 1; - else if (subm->tempfail) { - i_assert(subm->error != NULL); - *error_r = t_strdup(subm->error); - return -1; - } else { - i_assert(subm->error != NULL); - *error_r = t_strdup(subm->error); - return 0; +static void +smtp_submit_sendmail_callback(int status, struct smtp_submit *subm) +{ + if (status < 0) { + smtp_submit_callback(subm, -1, + "Failed to execute sendmail"); + return; + } + if (status == 0) { + smtp_submit_callback(subm, -1, + "Sendmail program returned error"); + return; } + + smtp_submit_callback(subm, 1, NULL); } -static int +static void smtp_submit_send_sendmail(struct smtp_submit *subm, - unsigned int timeout_secs, const char **error_r) + unsigned int timeout_secs) { + const struct smtp_submit_settings *set = &subm->session->set; const char *const *sendmail_args, *sendmail_bin, *str; ARRAY_TYPE(const_string) args; unsigned int i; struct program_client_settings pc_set; struct program_client *pc; - int ret; - sendmail_args = t_strsplit(subm->set.sendmail_path, " "); + sendmail_args = t_strsplit(set->sendmail_path, " "); t_array_init(&args, 16); i_assert(sendmail_args[0] != NULL); sendmail_bin = sendmail_args[0]; @@ -221,57 +344,75 @@ smtp_submit_send_sendmail(struct smtp_submit *subm, program_client_set_input(pc, subm->input); i_stream_unref(&subm->input); - ret = program_client_run(pc); + subm->prg_client = pc; - program_client_destroy(&pc); + program_client_run_async(pc, smtp_submit_sendmail_callback, subm); +} - if (ret < 0) { - *error_r = "Failed to execute sendmail"; - return -1; - } else if (ret == 0) { - *error_r = "Sendmail program returned error"; - return -1; - } - return 1; +struct smtp_submit_run_context { + int status; + char *error; +}; + +static void +smtp_submit_run_callback(const struct smtp_submit_result *result, + struct smtp_submit_run_context *rctx) +{ + rctx->error = i_strdup(result->error); + rctx->status = result->status; + io_loop_stop(current_ioloop); } -void smtp_submit_abort(struct smtp_submit **_subm) +int smtp_submit_run_timeout(struct smtp_submit *subm, + unsigned int timeout_secs, const char **error_r) { - struct smtp_submit *subm = *_subm; + struct smtp_submit_run_context rctx; + struct ioloop *ioloop; - *_subm = NULL; + ioloop = io_loop_create(); + io_loop_set_running(ioloop); - if (subm->output != NULL) { - o_stream_ignore_last_errors(subm->output); - o_stream_destroy(&subm->output); + i_zero(&rctx); + smtp_submit_run_async(subm, timeout_secs, + smtp_submit_run_callback, &rctx); + + if (io_loop_is_running(ioloop)) + io_loop_run(ioloop); + + io_loop_destroy(&ioloop); + + if (rctx.error == NULL) + *error_r = NULL; + else { + *error_r = t_strdup(rctx.error); + i_free(rctx.error); } - if (subm->input != NULL) - i_stream_destroy(&subm->input); - pool_unref(&subm->pool); + + return rctx.status; } -int smtp_submit_deinit(struct smtp_submit *subm, const char **error_r) +int smtp_submit_run(struct smtp_submit *submit, const char **error_r) { - return smtp_submit_deinit_timeout(subm, 0, error_r); + return smtp_submit_run_timeout(submit, 0, error_r); } -int smtp_submit_deinit_timeout(struct smtp_submit *subm, - unsigned int timeout_secs, const char **error_r) +#undef smtp_submit_run_async +void smtp_submit_run_async(struct smtp_submit *subm, + unsigned int timeout_secs, + smtp_submit_callback_t *callback, void *context) { - int ret; + const struct smtp_submit_settings *set = &subm->session->set; + + subm->callback = callback; + subm->context = context; /* the mail has been written to a file. now actually send it. */ subm->input = iostream_temp_finish (&subm->output, IO_BLOCK_SIZE); - if (subm->set.submission_host != NULL) { - ret = smtp_submit_send_host - (subm, timeout_secs, error_r); + if (set->submission_host != NULL) { + smtp_submit_send_host(subm, timeout_secs); } else { - ret = smtp_submit_send_sendmail - (subm, timeout_secs, error_r); + smtp_submit_send_sendmail(subm, timeout_secs); } - - smtp_submit_abort(&subm); - return ret; } diff --git a/src/lib-smtp/smtp-submit.h b/src/lib-smtp/smtp-submit.h index 25d255bdf2..c461397e36 100644 --- a/src/lib-smtp/smtp-submit.h +++ b/src/lib-smtp/smtp-submit.h @@ -1,25 +1,63 @@ #ifndef SMTP_SUBMIT_H #define SMTP_SUBMIT_H +struct smtp_submit_session; +struct smtp_submit; + struct smtp_submit_settings { const char *hostname; const char *submission_host; const char *sendmail_path; }; -struct smtp_submit * ATTR_NULL(3) -smtp_submit_init(const struct smtp_submit_settings *set, +struct smtp_submit_result { + /* 1 on success, + 0 on permanent failure (e.g. invalid destination), + -1 on temporary failure */ + int status; + + const char *error; +}; + +typedef void +smtp_submit_callback_t(const struct smtp_submit_result *result, + void *context); + +/* Use submit session to reuse resources (e.g. SMTP connections) between + submissions (FIXME: actually implement this) */ +struct smtp_submit_session * +smtp_submit_session_init(const struct smtp_submit_settings *set); +void smtp_submit_session_deinit(struct smtp_submit_session **_session); + +struct smtp_submit * +smtp_submit_init(struct smtp_submit_session *session, + const char *return_path); +struct smtp_submit * +smtp_submit_init_simple(const struct smtp_submit_settings *set, const char *return_path); +void smtp_submit_deinit(struct smtp_submit **_sct); + /* Add a new recipient */ void smtp_submit_add_rcpt(struct smtp_submit *subm, const char *address); /* Get an output stream where the message can be written to. The recipients must already be added before calling this. */ struct ostream *smtp_submit_send(struct smtp_submit *subm); -void smtp_submit_abort(struct smtp_submit **_subm); + +/* Submit the message. Callback is called once the message submission + finishes. */ +void smtp_submit_run_async(struct smtp_submit *subm, + unsigned int timeout_secs, + smtp_submit_callback_t *callback, void *context); +#define smtp_submit_run_async(subm, timeout_secs, callback, context) \ + smtp_submit_run_async(subm, timeout_secs, \ + (smtp_submit_callback_t*)callback, \ + (char*)context + CALLBACK_TYPECHECK(callback, \ + void (*)(const struct smtp_submit_result *result, typeof(context)))) + /* Returns 1 on success, 0 on permanent failure (e.g. invalid destination), -1 on temporary failure. */ -int smtp_submit_deinit(struct smtp_submit *subm, const char **error_r); -/* Same as smtp_submit_deinit(), but timeout after given number of seconds. */ -int smtp_submit_deinit_timeout(struct smtp_submit *subm, +int smtp_submit_run(struct smtp_submit *subm, const char **error_r); +/* Same as smtp_submit_run(), but timeout after given number of seconds. */ +int smtp_submit_run_timeout(struct smtp_submit *subm, unsigned int timeout_secs, const char **error_r); #endif diff --git a/src/lib-smtp/test-smtp-submit.c b/src/lib-smtp/test-smtp-submit.c index e96ada704b..d3215c909e 100644 --- a/src/lib-smtp/test-smtp-submit.c +++ b/src/lib-smtp/test-smtp-submit.c @@ -30,6 +30,17 @@ static const char *test_message1 = "From: sender@example.com\r\n" "\r\n" "Test message\r\n"; +static const char *test_message2 = + "Subject: Test message\r\n" + "To: rcpt@example.com\r\n" + "From: sender@example.com\r\n" + "\r\n" + "Test message Test message Test message Test message Test message\r\n" + "Test message Test message Test message Test message Test message\r\n" + "Test message Test message Test message Test message Test message\r\n" + "Test message Test message Test message Test message Test message\r\n" + "Test message Test message Test message Test message Test message\r\n" + "Test message Test message Test message Test message Test message\r\n"; /* * Types @@ -748,16 +759,18 @@ test_client_denied_second_rcpt(const struct smtp_submit_settings *submit_set) smtp_submit_set.submission_host = t_strdup_printf("127.0.0.1:%u", bind_ports[0]); - smtp_submit = smtp_submit_init(&smtp_submit_set, "sender@example.com"); + smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com"); smtp_submit_add_rcpt(smtp_submit, "rcpt2@example.com"); output = smtp_submit_send(smtp_submit); o_stream_send_str(output, test_message1); - ret = smtp_submit_deinit_timeout(smtp_submit, 5, &error); + ret = smtp_submit_run_timeout(smtp_submit, 1000, &error); test_out_reason("run (ret == 0)", ret == 0, error); + smtp_submit_deinit(&smtp_submit); + return FALSE; } @@ -1423,6 +1436,80 @@ test_client_successful_delivery(const struct smtp_submit_settings *submit_set) return FALSE; } +struct _parallel_delivery_client { + unsigned int count; +}; + +static void +test_client_parallel_delivery_callback(const struct smtp_submit_result *result, + struct _parallel_delivery_client *ctx) +{ + if (result->status <= 0) + i_error("Submit failed: %s", result->error); + + if (--ctx->count == 0) + io_loop_stop(current_ioloop); +} + +static bool +test_client_parallel_delivery(const struct smtp_submit_settings *submit_set) +{ + struct smtp_submit_settings smtp_submit_set; + struct _parallel_delivery_client *ctx; + struct smtp_submit *smtp_submit1, *smtp_submit2; + struct ostream *output; + struct ioloop *ioloop; + + ioloop = io_loop_create(); + + ctx = i_new(struct _parallel_delivery_client, 1); + ctx->count = 2; + + smtp_submit_set = *submit_set; + + /* submit 1 */ + smtp_submit_set.submission_host = + t_strdup_printf("127.0.0.1:%u", bind_ports[0]); + smtp_submit1 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); + + smtp_submit_add_rcpt(smtp_submit1, "rcpt@example.com"); + output = smtp_submit_send(smtp_submit1); + o_stream_send_str(output, test_message1); + + smtp_submit_run_async(smtp_submit1, 5, + test_client_parallel_delivery_callback, ctx); + + /* submit 2 */ + smtp_submit_set.submission_host = + t_strdup_printf("127.0.0.1:%u", bind_ports[1]); + smtp_submit2 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); + + smtp_submit_add_rcpt(smtp_submit2, "rcpt@example.com"); + output = smtp_submit_send(smtp_submit2); + o_stream_send_str(output, test_message2); + + smtp_submit_run_async(smtp_submit2, 5, + test_client_parallel_delivery_callback, ctx); + + io_loop_run(ioloop); + + smtp_submit_deinit(&smtp_submit1); + smtp_submit_deinit(&smtp_submit2); + io_loop_destroy(&ioloop); + + /* verify delivery */ + test_message_delivery(test_message1, + t_strdup_printf("%s/message-%u.eml", + test_tmp_dir_get(), bind_ports[0])); + test_message_delivery(test_message2, + t_strdup_printf("%s/message-%u.eml", + test_tmp_dir_get(), bind_ports[1])); + + i_free(ctx); + + return FALSE; +} + /* test */ static void test_successful_delivery(void) @@ -1437,6 +1524,13 @@ static void test_successful_delivery(void) test_client_successful_delivery, test_server_successful_delivery, 1); test_end(); + + test_begin("parallel delivery"); + test_expect_errors(0); + test_run_client_server(&smtp_submit_set, + test_client_parallel_delivery, + test_server_successful_delivery, 2); + test_end(); } /* @@ -1459,15 +1553,17 @@ test_client_failed_sendmail(const struct smtp_submit_settings *submit_set) smtp_submit_set = *submit_set; smtp_submit_set.sendmail_path = sendmail_path; - smtp_submit = smtp_submit_init(&smtp_submit_set, "sender@example.com"); + smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com"); output = smtp_submit_send(smtp_submit); o_stream_send_str(output, test_message1); - ret = smtp_submit_deinit_timeout(smtp_submit, 5, &error); + ret = smtp_submit_run_timeout(smtp_submit, 5, &error); test_out_reason("run (ret < 0)", ret < 0, error); + smtp_submit_deinit(&smtp_submit); + return FALSE; } @@ -1509,15 +1605,17 @@ test_client_successful_sendmail(const struct smtp_submit_settings *submit_set) smtp_submit_set = *submit_set; smtp_submit_set.sendmail_path = sendmail_path; - smtp_submit = smtp_submit_init(&smtp_submit_set, "sender@example.com"); + smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com"); output = smtp_submit_send(smtp_submit); o_stream_send_str(output, test_message1); - ret = smtp_submit_deinit_timeout(smtp_submit, 5, &error); + ret = smtp_submit_run_timeout(smtp_submit, 5, &error); test_out_reason("run (ret > 0)", ret > 0, error); + smtp_submit_deinit(&smtp_submit); + /* verify delivery */ test_message_delivery(test_message1, msg_path); @@ -1539,6 +1637,103 @@ static void test_successful_sendmail(void) test_end(); } +/* + * Parallel sendmail + */ + +/* client */ + +struct _parallel_sendmail_client { + unsigned int count; +}; + +static void +test_client_parallel_sendmail_callback(const struct smtp_submit_result *result, + struct _parallel_sendmail_client *ctx) +{ + if (result->status <= 0) + i_error("Submit failed: %s", result->error); + + if (--ctx->count == 0) + io_loop_stop(current_ioloop); +} + +static bool +test_client_parallel_sendmail(const struct smtp_submit_settings *submit_set) +{ + struct smtp_submit_settings smtp_submit_set; + struct _parallel_sendmail_client *ctx; + struct smtp_submit *smtp_submit1, *smtp_submit2; + struct ostream *output; + const char *sendmail_path1, *sendmail_path2; + const char *msg_path1, *msg_path2; + struct ioloop *ioloop; + + ctx = i_new(struct _parallel_sendmail_client, 1); + ctx->count = 2; + + ioloop = io_loop_create(); + + msg_path1 = t_strdup_printf("%s/message1.eml", test_tmp_dir_get()); + msg_path2 = t_strdup_printf("%s/message2.eml", test_tmp_dir_get()); + + sendmail_path1 = t_strdup_printf( + TEST_BIN_DIR"/sendmail-success.sh %s", msg_path1); + sendmail_path2 = t_strdup_printf( + TEST_BIN_DIR"/sendmail-success.sh %s", msg_path2); + + smtp_submit_set = *submit_set; + + /* submit 1 */ + smtp_submit_set.sendmail_path = sendmail_path1; + smtp_submit1 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); + + smtp_submit_add_rcpt(smtp_submit1, "rcpt@example.com"); + output = smtp_submit_send(smtp_submit1); + o_stream_send_str(output, test_message1); + + smtp_submit_run_async(smtp_submit1, 5, + test_client_parallel_sendmail_callback, ctx); + + /* submit 2 */ + smtp_submit_set.sendmail_path = sendmail_path2; + smtp_submit2 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); + + smtp_submit_add_rcpt(smtp_submit2, "rcpt@example.com"); + output = smtp_submit_send(smtp_submit2); + o_stream_send_str(output, test_message2); + + smtp_submit_run_async(smtp_submit2, 5, + test_client_parallel_sendmail_callback, ctx); + + io_loop_run(ioloop); + + smtp_submit_deinit(&smtp_submit1); + smtp_submit_deinit(&smtp_submit2); + io_loop_destroy(&ioloop); + + /* verify delivery */ + test_message_delivery(test_message1, msg_path1); + test_message_delivery(test_message2, msg_path2); + + return FALSE; +} + +/* test */ + +static void test_parallel_sendmail(void) +{ + struct smtp_submit_settings smtp_submit_set; + + test_client_defaults(&smtp_submit_set); + + test_begin("parallel sendmail"); + test_expect_errors(0); + test_run_client_server(&smtp_submit_set, + test_client_parallel_sendmail, NULL, 0); + test_end(); +} + /* * All tests */ @@ -1560,6 +1755,7 @@ static void (*const test_functions[])(void) = { test_successful_delivery, test_failed_sendmail, test_successful_sendmail, + test_parallel_sendmail, NULL }; @@ -1588,19 +1784,23 @@ test_client_smtp_send_simple(const struct smtp_submit_settings *smtp_set, struct smtp_submit_settings smtp_submit_set; struct smtp_submit *smtp_submit; struct ostream *output; + int ret; /* send the message */ smtp_submit_set = *smtp_set; smtp_submit_set.submission_host = host, - smtp_submit = smtp_submit_init(&smtp_submit_set, "sender@example.com"); + smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com"); smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com"); output = smtp_submit_send(smtp_submit); o_stream_send_str(output, message); - return smtp_submit_deinit_timeout - (smtp_submit, timeout_secs, error_r); + ret = smtp_submit_run_timeout(smtp_submit, timeout_secs, error_r); + + smtp_submit_deinit(&smtp_submit); + + return ret; } static int