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);
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),
orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
str_sanitize(error, 512));
}
+ smtp_submit_deinit(&smtp_submit);
return ret < 0 ? -1 : 0;
}
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;
}
*_client = NULL;
- smtp_submit_abort(&client->submit);
+ smtp_submit_deinit(&client->submit);
i_free(client);
}
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;
}
#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);
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) {
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];
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;
}
#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
"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
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;
}
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)
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();
}
/*
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;
}
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);
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
*/
test_successful_delivery,
test_failed_sendmail,
test_successful_sendmail,
+ test_parallel_sendmail,
NULL
};
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