Mercurial > dovecot > core-2.2
changeset 12752:be78b9fd88e7
lib-lda: Added submission_host setting to send mails via SMTP instead of sendmail.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 22 Feb 2011 13:57:20 +0200 |
parents | 3053befe6a64 |
children | bc77e80947c0 |
files | doc/example-config/conf.d/15-lda.conf src/lib-lda/lda-settings.c src/lib-lda/lda-settings.h src/lib-lda/smtp-client.c |
diffstat | 4 files changed, 198 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/example-config/conf.d/15-lda.conf Tue Feb 22 11:59:57 2011 +0200 +++ b/doc/example-config/conf.d/15-lda.conf Tue Feb 22 13:57:20 2011 +0200 @@ -17,6 +17,9 @@ # Binary to use for sending mails. #sendmail_path = /usr/sbin/sendmail +# If non-empty, send mails via this SMTP host[:port] instead of sendmail. +#submission_host = + # Subject: header to use for rejection mails. You can use the same variables # as for rejection_reason below. #rejection_subject = Rejected: %s
--- a/src/lib-lda/lda-settings.c Tue Feb 22 11:59:57 2011 +0200 +++ b/src/lib-lda/lda-settings.c Tue Feb 22 13:57:20 2011 +0200 @@ -20,6 +20,7 @@ static const struct setting_define lda_setting_defines[] = { DEF(SET_STR, postmaster_address), DEF(SET_STR, hostname), + DEF(SET_STR, submission_host), DEF(SET_STR, sendmail_path), DEF(SET_STR, rejection_subject), DEF(SET_STR, rejection_reason), @@ -36,6 +37,7 @@ static const struct lda_settings lda_default_settings = { .postmaster_address = "", .hostname = "", + .submission_host = "", .sendmail_path = "/usr/sbin/sendmail", .rejection_subject = "Rejected: %s", .rejection_reason =
--- a/src/lib-lda/lda-settings.h Tue Feb 22 11:59:57 2011 +0200 +++ b/src/lib-lda/lda-settings.h Tue Feb 22 13:57:20 2011 +0200 @@ -6,12 +6,14 @@ struct lda_settings { const char *postmaster_address; const char *hostname; + const char *submission_host; const char *sendmail_path; const char *rejection_subject; const char *rejection_reason; const char *deliver_log_format; const char *recipient_delimiter; const char *lda_original_recipient_header; + bool quota_full_tempfail; bool lda_mailbox_autocreate; bool lda_mailbox_autosubscribe;
--- a/src/lib-lda/smtp-client.c Tue Feb 22 11:59:57 2011 +0200 +++ b/src/lib-lda/smtp-client.c Tue Feb 22 13:57:20 2011 +0200 @@ -1,8 +1,14 @@ /* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "close-keep-errno.h" +#include "safe-mkstemp.h" #include "execv-const.h" +#include "istream.h" #include "master-service.h" +#include "lmtp-client.h" #include "lda-settings.h" #include "mail-deliver.h" #include "smtp-client.h" @@ -11,9 +17,20 @@ #include <sys/wait.h> #include <sysexits.h> +#define DEFAULT_SUBMISSION_PORT 25 + struct smtp_client { FILE *f; pid_t pid; + + bool use_smtp; + bool success; + bool finished; + + const struct lda_settings *set; + char *temp_path; + char *destination; + char *return_path; }; static struct smtp_client *smtp_client_devnull(FILE **file_r) @@ -56,9 +73,10 @@ execv_const(sendmail_path, argv); } -struct smtp_client * -smtp_client_open(const struct lda_settings *set, const char *destination, - const char *return_path, FILE **file_r) +static struct smtp_client * +smtp_client_open_sendmail(const struct lda_settings *set, + const char *destination, const char *return_path, + FILE **file_r) { struct smtp_client *client; int fd[2]; @@ -88,11 +106,68 @@ return client; } -int smtp_client_close(struct smtp_client *client) +static int create_temp_file(const char **path_r) +{ + string_t *path; + int fd; + + path = t_str_new(128); + str_append(path, "/tmp/dovecot."); + str_append(path, master_service_get_name(master_service)); + str_append_c(path, '.'); + + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + i_error("safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* we just want the fd, unlink it */ + if (unlink(str_c(path)) < 0) { + /* shouldn't happen.. */ + i_error("unlink(%s) failed: %m", str_c(path)); + close_keep_errno(fd); + return -1; + } + + *path_r = str_c(path); + return fd; +} + +struct smtp_client * +smtp_client_open(const struct lda_settings *set, const char *destination, + const char *return_path, FILE **file_r) +{ + struct smtp_client *client; + const char *path; + int fd; + + if (*set->submission_host == '\0') { + return smtp_client_open_sendmail(set, destination, + return_path, file_r); + } + + if ((fd = create_temp_file(&path)) == -1) + return smtp_client_devnull(file_r); + + client = i_new(struct smtp_client, 1); + client->set = set; + client->temp_path = i_strdup(path); + client->destination = i_strdup(destination); + client->return_path = i_strdup(return_path); + client->f = *file_r = fdopen(fd, "w"); + if (client->f == NULL) + i_fatal("fdopen() failed: %m"); + client->use_smtp = TRUE; + return client; +} + +static int smtp_client_close_sendmail(struct smtp_client *client) { int ret = EX_TEMPFAIL, status; fclose(client->f); + if (client->pid == (pid_t)-1) { /* smtp_client_open() failed already */ } else if (waitpid(client->pid, &status, 0) < 0) @@ -113,7 +188,118 @@ i_error("Sendmail process terminated abnormally, " "return status %d", status); } - i_free(client); return ret; } + +static void smtp_client_send_finished(void *context) +{ + struct smtp_client *smtp_client = context; + + smtp_client->finished = TRUE; + io_loop_stop(current_ioloop); +} + +static void +rcpt_to_callback(bool success, const char *reply, void *context) +{ + struct smtp_client *smtp_client = context; + + if (!success) { + i_error("smtp(%s): RCPT TO failed: %s", + smtp_client->set->submission_host, reply); + smtp_client_send_finished(smtp_client); + } +} + +static void +data_callback(bool success, const char *reply, void *context) +{ + struct smtp_client *smtp_client = context; + + if (!success) { + i_error("smtp(%s): DATA failed: %s", + smtp_client->set->submission_host, reply); + smtp_client_send_finished(smtp_client); + } else { + smtp_client->success = TRUE; + } +} + +static int smtp_client_send(struct smtp_client *smtp_client) +{ + struct lmtp_client_settings client_set; + struct lmtp_client *client; + struct ioloop *ioloop; + struct istream *input; + const char *host, *p; + unsigned int port = DEFAULT_SUBMISSION_PORT; + + host = smtp_client->set->submission_host; + p = strchr(host, ':'); + if (p != NULL) { + host = t_strdup_until(host, p); + if (str_to_uint(p + 1, &port) < 0 || + port == 0 || port > 65535) { + i_error("Invalid port in submission_host: %s", p+1); + return -1; + } + } + + if (fflush(smtp_client->f) != 0) { + i_error("fflush(%s) failed: %m", smtp_client->temp_path); + return -1; + } + + if (lseek(fileno(smtp_client->f), 0, SEEK_SET) < 0) { + i_error("lseek(%s) failed: %m", smtp_client->temp_path); + return -1; + } + + memset(&client_set, 0, sizeof(client_set)); + client_set.mail_from = smtp_client->return_path == NULL ? "<>" : + t_strconcat("<", smtp_client->return_path, ">", NULL); + client_set.my_hostname = smtp_client->set->hostname; + client_set.dns_client_socket_path = "dns-client"; + + ioloop = io_loop_create(); + client = lmtp_client_init(&client_set, smtp_client_send_finished, + smtp_client); + + if (lmtp_client_connect_tcp(client, LMTP_CLIENT_PROTOCOL_SMTP, + host, port) < 0) { + lmtp_client_deinit(&client); + io_loop_destroy(&ioloop); + return -1; + } + + lmtp_client_add_rcpt(client, smtp_client->destination, + rcpt_to_callback, data_callback, smtp_client); + + input = i_stream_create_fd(fileno(smtp_client->f), (size_t)-1, FALSE); + lmtp_client_send(client, input); + i_stream_unref(&input); + + if (!smtp_client->finished) + io_loop_run(ioloop); + io_loop_destroy(&ioloop); + return smtp_client->success ? 0 : -1; +} + +int smtp_client_close(struct smtp_client *client) +{ + int ret; + + if (!client->use_smtp) + return smtp_client_close_sendmail(client); + + /* the mail has been written to a file. now actually send it. */ + ret = smtp_client_send(client); + + fclose(client->f); + i_free(client->return_path); + i_free(client->destination); + i_free(client->temp_path); + i_free(client); + return ret < 0 ? EX_TEMPFAIL : 0; +}