summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/enqueue.c
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2014-10-26 11:26:09 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2014-10-26 11:26:09 +0000
commitbbf7137d5477b89befe15ac8e7680cfa709c696c (patch)
treeb1ef872296d854dd45e0abe9acb7e061df167300 /usr.sbin/smtpd/enqueue.c
parentfb747a2e9e1cbcaba9d08e7f4977578ad7ff8bfa (diff)
when using the local enqueuer, if the internal SMTP session fails, copy the
original message to ~/dead.letter so it's not lost
Diffstat (limited to 'usr.sbin/smtpd/enqueue.c')
-rw-r--r--usr.sbin/smtpd/enqueue.c146
1 files changed, 122 insertions, 24 deletions
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index 05983e41ba5..00ca3bc8ba1 100644
--- a/usr.sbin/smtpd/enqueue.c
+++ b/usr.sbin/smtpd/enqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: enqueue.c,v 1.85 2014/10/08 10:56:01 deraadt Exp $ */
+/* $OpenBSD: enqueue.c,v 1.86 2014/10/26 11:26:08 gilles Exp $ */
/*
* Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -51,9 +51,10 @@ static void parse_addr_terminal(int);
static char *qualify_addr(char *);
static void rcpt_add(char *);
static int open_connection(void);
-static void get_responses(FILE *, int);
+static int get_responses(FILE *, int);
static int send_line(FILE *, int, char *, ...);
static int enqueue_offline(int, char *[], FILE *);
+static int savedeadletter(struct passwd *, FILE *);
extern int srv_connect(void);
@@ -120,6 +121,7 @@ struct {
int saw_content_disposition;
int saw_content_transfer_encoding;
int saw_user_agent;
+ int noheader;
} msg;
struct {
@@ -204,7 +206,7 @@ send_header(FILE *fout, const char *line, size_t len)
int
enqueue(int argc, char *argv[])
{
- int i, ch, tflag = 0, noheader;
+ int i, ch, tflag = 0;
char *fake_from = NULL, *buf;
struct passwd *pw;
FILE *fp, *fout;
@@ -297,7 +299,7 @@ enqueue(int argc, char *argv[])
errc(EX_UNAVAILABLE, saved_errno, "mkstemp");
}
unlink(sfn);
- noheader = parse_message(stdin, fake_from == NULL, tflag, fp);
+ msg.noheader = parse_message(stdin, fake_from == NULL, tflag, fp);
if (msg.rcpt_cnt == 0)
errx(EX_SOFTWARE, "no recipients");
@@ -324,10 +326,12 @@ enqueue(int argc, char *argv[])
*/
/* banner */
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
send_line(fout, verbose, "EHLO localhost\n");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
if (msg.dsn_envid != NULL)
envid_sz = strlen(msg.dsn_envid);
@@ -338,18 +342,21 @@ enqueue(int argc, char *argv[])
msg.dsn_ret ? msg.dsn_ret : "",
envid_sz ? "ENVID=" : "",
envid_sz ? msg.dsn_envid : "");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
for (i = 0; i < msg.rcpt_cnt; i++) {
send_line(fout, verbose, "RCPT TO:<%s> %s%s\n",
msg.rcpts[i],
msg.dsn_notify ? "NOTIFY=" : "",
msg.dsn_notify ? msg.dsn_notify : "");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
}
send_line(fout, verbose, "DATA\n");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
/* add From */
if (!msg.saw_from)
@@ -382,7 +389,7 @@ enqueue(int argc, char *argv[])
}
/* add separating newline */
- if (noheader)
+ if (msg.noheader)
send_line(fout, 0, "\n");
else
inheaders = 1;
@@ -405,7 +412,7 @@ enqueue(int argc, char *argv[])
line = buf;
- if (msg.saw_content_transfer_encoding || noheader ||
+ if (msg.saw_content_transfer_encoding || msg.noheader ||
inheaders || !msg.need_linesplit) {
if (inheaders)
send_header(fout, line, len);
@@ -432,18 +439,25 @@ enqueue(int argc, char *argv[])
} while (len);
}
send_line(fout, verbose, ".\n");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
send_line(fout, verbose, "QUIT\n");
- get_responses(fout, 1);
+ if (! get_responses(fout, 1))
+ goto fail;
fclose(fp);
fclose(fout);
exit(EX_OK);
+
+fail:
+ if (pw)
+ savedeadletter(pw, fp);
+ exit(EX_SOFTWARE);
}
-static void
+static int
get_responses(FILE *fin, int n)
{
char *buf;
@@ -451,34 +465,45 @@ get_responses(FILE *fin, int n)
int e;
fflush(fin);
- if ((e = ferror(fin)))
- errx(1, "ferror: %d", e);
+ if ((e = ferror(fin))) {
+ warnx("ferror: %d", e);
+ return 0;
+ }
while (n) {
buf = fgetln(fin, &len);
- if (buf == NULL && ferror(fin))
- err(1, "fgetln");
+ if (buf == NULL && ferror(fin)) {
+ warn("fgetln");
+ return 0;
+ }
if (buf == NULL && feof(fin))
break;
- if (buf == NULL || len < 1)
- err(1, "fgetln weird");
+ if (buf == NULL || len < 1) {
+ warn("fgetln weird");
+ return 0;
+ }
/* account for \r\n linebreaks */
if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n')
buf[--len - 1] = '\n';
- if (len < 4)
- errx(1, "bad response");
+ if (len < 4) {
+ warnx("bad response");
+ return 0;
+ }
if (verbose)
printf("<<< %.*s", (int)len, buf);
if (buf[3] == '-')
continue;
- if (buf[0] != '2' && buf[0] != '3')
- errx(1, "command failed: %.*s", (int)len, buf);
+ if (buf[0] != '2' && buf[0] != '3') {
+ warnx("command failed: %.*s", (int)len, buf);
+ return 0;
+ }
n--;
}
+ return 1;
}
static int
@@ -860,3 +885,76 @@ enqueue_offline(int argc, char *argv[], FILE *ifile)
return (EX_TEMPFAIL);
}
+
+static int
+savedeadletter(struct passwd *pw, FILE *in)
+{
+ char buffer[SMTPD_MAXPATHLEN];
+ FILE *fp;
+ char *buf, *lbuf;
+ size_t len;
+
+ (void)snprintf(buffer, sizeof buffer, "%s/dead.letter", pw->pw_dir);
+
+ if (fseek(in, 0, SEEK_SET) != 0)
+ return 0;
+
+ if ((fp = fopen(buffer, "a")) == NULL)
+ return 0;
+
+ /* add From */
+ if (!msg.saw_from)
+ fprintf(fp, "From: %s%s<%s>\n",
+ msg.fromname ? msg.fromname : "",
+ msg.fromname ? " " : "",
+ msg.from);
+
+ /* add Date */
+ if (!msg.saw_date)
+ fprintf(fp, "Date: %s\n", time_to_text(timestamp));
+
+ /* add Message-Id */
+ if (!msg.saw_msgid)
+ fprintf(fp, "Message-Id: <%"PRIu64".enqueue@%s>\n",
+ generate_uid(), host);
+
+ if (msg.need_linesplit) {
+ /* we will always need to mime encode for long lines */
+ if (!msg.saw_mime_version)
+ fprintf(fp, "MIME-Version: 1.0\n");
+ if (!msg.saw_content_type)
+ fprintf(fp, "Content-Type: text/plain; "
+ "charset=unknown-8bit\n");
+ if (!msg.saw_content_disposition)
+ fprintf(fp, "Content-Disposition: inline\n");
+ if (!msg.saw_content_transfer_encoding)
+ fprintf(fp, "Content-Transfer-Encoding: "
+ "quoted-printable\n");
+ }
+
+ /* add separating newline */
+ if (msg.noheader)
+ fprintf(fp, "\n");
+
+
+ lbuf = NULL;
+ while ((buf = fgetln(in, &len))) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else {
+ /* EOF without EOL, copy and add the NUL */
+ if ((lbuf = malloc(len + 1)) == NULL)
+ err(1, NULL);
+ memcpy(lbuf, buf, len);
+ lbuf[len] = '\0';
+ buf = lbuf;
+ }
+ fprintf(fp, "%s\n", buf);
+ }
+ free(lbuf);
+
+ fprintf(fp, "\n");
+ fclose(fp);
+
+ return 1;
+}