dovecot-2.0: lib-lda: Added submission_host setting to send mail...

dovecot at dovecot.org dovecot at dovecot.org
Tue Feb 22 13:57:30 EET 2011


details:   http://hg.dovecot.org/dovecot-2.0/rev/be78b9fd88e7
changeset: 12617:be78b9fd88e7
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Feb 22 13:57:20 2011 +0200
description:
lib-lda: Added submission_host setting to send mails via SMTP instead of sendmail.

diffstat:

 doc/example-config/conf.d/15-lda.conf |    3 +
 src/lib-lda/lda-settings.c            |    2 +
 src/lib-lda/lda-settings.h            |    2 +
 src/lib-lda/smtp-client.c             |  196 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 198 insertions(+), 5 deletions(-)

diffs (293 lines):

diff -r 3053befe6a64 -r be78b9fd88e7 doc/example-config/conf.d/15-lda.conf
--- 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
diff -r 3053befe6a64 -r be78b9fd88e7 src/lib-lda/lda-settings.c
--- 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 =
diff -r 3053befe6a64 -r be78b9fd88e7 src/lib-lda/lda-settings.h
--- 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;
diff -r 3053befe6a64 -r be78b9fd88e7 src/lib-lda/smtp-client.c
--- 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;
+}


More information about the dovecot-cvs mailing list