diff options
Diffstat (limited to 'usr.sbin/smtpd/enqueue.c')
-rw-r--r-- | usr.sbin/smtpd/enqueue.c | 835 |
1 files changed, 590 insertions, 245 deletions
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index 60e4349ce73..bff3bcb90f6 100644 --- a/usr.sbin/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,7 +1,7 @@ -/* $OpenBSD: enqueue.c,v 1.11 2009/04/05 16:10:42 gilles Exp $ */ +/* $OpenBSD: enqueue.c,v 1.12 2009/04/16 15:35:06 jacekm Exp $ */ /* - * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> + * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -11,26 +11,25 @@ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> #include <sys/param.h> +#include <sys/queue.h> #include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> -#include <arpa/inet.h> +#include <sys/tree.h> +#include <sys/types.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <event.h> +#include <netdb.h> #include <pwd.h> +#include <signal.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,48 +39,136 @@ extern struct imsgbuf *ibuf; -__dead void usage(void); -int enqueue(int, char **); -int enqueue_init(struct message *); -int enqueue_add_recipient(struct message *, char *); -int enqueue_messagefd(struct message *); -int enqueue_write_message(FILE *, FILE *); -int enqueue_commit(struct message *); +void usage(void); +void sighdlr(int); +int main(int, char *[]); +void femail_write(const void *, size_t); +void femail_put(const char *, ...); +void send_cmd(const char *); +void build_from(char *, struct passwd *); +int parse_message(FILE *, int, int); +void parse_addr(char *, size_t, int); +void parse_addr_terminal(int); +char *qualify_addr(char *); +void rcpt_add(char *); +void received(void); +int open_connection(void); +int read_reply(void); +void greeting(int); +void mailfrom(char *); +void rcptto(char *); +void start_data(void); +void send_message(int); +void end_data(void); +int enqueue(int, char **); + +enum headerfields { + HDR_NONE, + HDR_FROM, + HDR_TO, + HDR_CC, + HDR_BCC, + HDR_SUBJECT, + HDR_DATE, + HDR_MSGID +}; + +struct { + char *word; + enum headerfields type; +} keywords[] = { + { "From:", HDR_FROM }, + { "To:", HDR_TO }, + { "Cc:", HDR_CC }, + { "Bcc:", HDR_BCC }, + { "Subject:", HDR_SUBJECT }, + { "Date:", HDR_DATE }, + { "Message-Id:", HDR_MSGID } +}; + +#define STATUS_GREETING 220 +#define STATUS_HELO 250 +#define STATUS_MAILFROM 250 +#define STATUS_RCPTTO 250 +#define STATUS_DATA 354 +#define STATUS_QUEUED 250 +#define STATUS_QUIT 221 +#define SMTP_LINELEN 1000 +#define SMTP_TIMEOUT 120 +#define TIMEOUTMSG "Timeout\n" + +#define WSP(c) (c == ' ' || c == '\t') + +int verbose = 0; +char host[MAXHOSTNAMELEN]; +char *user = NULL; +time_t timestamp; + +struct { + int fd; + char *from; + char *fromname; + char **rcpts; + int rcpt_cnt; + char *data; + size_t len; + int saw_date; + int saw_msgid; + int saw_from; +} msg; + +struct { + u_int quote; + u_int comment; + u_int esc; + u_int brackets; + size_t wpos; + char buf[SMTP_LINELEN]; +} pstate; + +void +sighdlr(int sig) +{ + if (sig == SIGALRM) { + write(STDERR_FILENO, TIMEOUTMSG, sizeof(TIMEOUTMSG)); + _exit (2); + } +} int enqueue(int argc, char *argv[]) { - int ch; - int fd; - FILE *fpout; - struct message message; - char sender[MAX_PATH_SIZE]; - - uid_t uid; - char *username; - char hostname[MAXHOSTNAMELEN]; - struct passwd *pw; - - uid = getuid(); - pw = safe_getpwuid(uid); - if (pw == NULL) - errx(1, "you don't exist, go away."); - - username = pw->pw_name; - gethostname(hostname, sizeof(hostname)); + int i, ch, tflag = 0, status, noheader; + char *fake_from = NULL; + struct passwd *pw; - if (! bsnprintf(sender, sizeof(sender), "%s@%s", username, hostname)) - errx(1, "sender address too long."); + bzero(&msg, sizeof(msg)); + time(×tamp); - while ((ch = getopt(argc, argv, "f:i")) != -1) { + while ((ch = getopt(argc, argv, "46B:b:E::e:F:f:iJ::mo:p:tvx")) != -1) { switch (ch) { case 'f': - if (strlcpy(sender, optarg, sizeof(sender)) - >= sizeof(sender)) - errx(1, "sender address too long."); + fake_from = optarg; break; - case 'i': /* ignore, interface compatibility */ + case 'F': + msg.fromname = optarg; + break; + case 't': + tflag = 1; + break; + case 'v': + verbose = 1; + break; + /* all remaining: ignored, sendmail compat */ + case 'B': + case 'b': + case 'E': + case 'e': + case 'i': + case 'm': case 'o': + case 'p': + case 'x': break; default: usage(); @@ -91,165 +178,350 @@ enqueue(int argc, char *argv[]) argc -= optind; argv += optind; - bzero(&message, sizeof(struct message)); - - strlcpy(message.session_helo, "localhost", - sizeof(message.session_helo)); - strlcpy(message.session_hostname, hostname, - sizeof(message.session_hostname)); - - /* build sender */ - if (! recipient_to_path(&message.sender, sender)) - errx(1, "invalid sender address."); - - if (! enqueue_init(&message)) - errx(1, "failed to initialize enqueue message."); - - if (argc == 0) - errx(1, "no recipient."); - - while (argc--) { - if (! enqueue_add_recipient(&message, *argv)) - errx(1, "invalid recipient."); - ++argv; + if (gethostname(host, sizeof(host)) == -1) + err(1, "gethostname"); + if ((pw = getpwuid(getuid())) == NULL) + user = "anonymous"; + if (pw != NULL && (user = strdup(pw->pw_name)) == NULL) + err(1, "strdup"); + + build_from(fake_from, pw); + + while(argc > 0) { + rcpt_add(argv[0]); + argv++; + argc--; } - fd = enqueue_messagefd(&message); - if (fd == -1 || (fpout = fdopen(fd, "w")) == NULL) - errx(1, "failed to open message file for writing."); + noheader = parse_message(stdin, fake_from == NULL, tflag); - if (! enqueue_write_message(stdin, fpout)) - errx(1, "failed to write message to message file."); + if (msg.rcpt_cnt == 0) + errx(1, "no recipients"); - if (! safe_fclose(fpout)) - errx(1, "error while writing to message file."); + signal(SIGALRM, sighdlr); + alarm(SMTP_TIMEOUT); - if (! enqueue_commit(&message)) - errx(1, "failed to commit message to queue."); + msg.fd = open_connection(); + if ((status = read_reply()) != STATUS_GREETING) + errx(1, "server greets us with status %d", status); + greeting(1); + mailfrom(msg.from); + for (i = 0; i < msg.rcpt_cnt; i++) + rcptto(msg.rcpts[i]); + start_data(); + send_message(noheader); + end_data(); - return 0; + close(msg.fd); + exit (0); } -int -enqueue_add_recipient(struct message *messagep, char *recipient) +void +femail_write(const void *buf, size_t nbytes) +{ + ssize_t n; + + do { + n = write(msg.fd, buf, nbytes); + } while (n == -1 && errno == EINTR); + + if (n == 0) + errx(1, "write: connection closed"); + if (n == -1) + err(1, "write"); + if ((size_t)n < nbytes) + errx(1, "short write: %ld of %lu bytes written", + (long)n, (u_long)nbytes); +} + +void +femail_put(const char *fmt, ...) +{ + va_list ap; + char buf[SMTP_LINELEN]; + + va_start(ap, fmt); + if (vsnprintf(buf, sizeof(buf), fmt, ap) >= (int)sizeof(buf)) + errx(1, "line length exceeded"); + va_end(ap); + + femail_write(buf, strlen(buf)); +} + +void +send_cmd(const char *cmd) +{ + if (verbose) + printf(">>> %s\n", cmd); + + femail_put("%s\r\n", cmd); +} + +void +build_from(char *fake_from, struct passwd *pw) { - char buffer[MAX_PATH_SIZE]; - struct message_recipient mr; - struct sockaddr_in6 *ssin6; - struct sockaddr_in *ssin; - struct message message; - int done = 0; - int n; - struct imsg imsg; - - bzero(&mr, sizeof(mr)); - - message = *messagep; - - if (strlcpy(buffer, recipient, sizeof(buffer)) >= sizeof(buffer)) - errx(1, "recipient address too long."); - - if (strchr(buffer, '@') == NULL) { - if (! bsnprintf(buffer, sizeof(buffer), "%s@%s", - buffer, messagep->sender.domain)) - errx(1, "recipient address too long."); + char *p; + + if (fake_from == NULL) + msg.from = qualify_addr(user); + else { + if (fake_from[0] == '<') { + if (fake_from[strlen(fake_from) - 1] != '>') + errx(1, "leading < but no trailing >"); + fake_from[strlen(fake_from) - 1] = 0; + if ((p = malloc(strlen(fake_from))) == NULL) + err(1, "malloc"); + strlcpy(p, fake_from + 1, strlen(fake_from)); + + msg.from = qualify_addr(p); + free(p); + } else + msg.from = qualify_addr(fake_from); } - - if (! recipient_to_path(&message.recipient, buffer)) - errx(1, "invalid recipient address."); - - message.session_rcpt = message.recipient; - - mr.ss.ss_family = AF_INET6; - mr.ss.ss_len = sizeof(*ssin6); - ssin6 = (struct sockaddr_in6 *)&mr.ss; - if (inet_pton(AF_INET6, "::1", &ssin6->sin6_addr) != 1) { - mr.ss.ss_family = AF_INET; - mr.ss.ss_len = sizeof(*ssin); - ssin = (struct sockaddr_in *)&mr.ss; - if (inet_pton(AF_INET, "127.0.0.1", &ssin->sin_addr) != 1) - return 0; + + if (msg.fromname == NULL && fake_from == NULL && pw != NULL) { + size_t len; + + len = strcspn(pw->pw_gecos, ","); + len++; /* null termination */ + if ((msg.fromname = malloc(len)) == NULL) + err(1, NULL); + strlcpy(msg.fromname, pw->pw_gecos, len); } - message.session_ss = mr.ss; +} - mr.path = message.recipient; - mr.id = message.session_id; - mr.msg = message; - mr.msg.flags |= F_MESSAGE_ENQUEUED; +int +parse_message(FILE *fin, int get_from, int tflag) +{ + char *buf, *twodots = ".."; + size_t len, new_len; + void *newp; + u_int i, cur = HDR_NONE, dotonly; + u_int header_seen = 0, header_done = 0; + + bzero(&pstate, sizeof(pstate)); + for (;;) { + buf = fgetln(fin, &len); + if (buf == NULL && ferror(fin)) + err(1, "fgetln"); + if (buf == NULL && feof(fin)) + break; - imsg_compose(ibuf, IMSG_MFA_RCPT, 0, 0, -1, &mr, sizeof (mr)); - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) - err(1, "write error"); + /* account for \r\n linebreaks */ + if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') + buf[--len - 1] = '\n'; - while (!done) { - if ((n = imsg_read(ibuf)) == -1) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); + if (len == 1 && buf[0] == '\n') /* end of header */ + header_done = 1; - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); + if (buf == NULL || len < 1) + err(1, "fgetln weird"); - if (n == 0) - continue; + if (!WSP(buf[0])) { /* whitespace -> continuation */ + if (cur == HDR_FROM) + parse_addr_terminal(1); + if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC) + parse_addr_terminal(0); + cur = HDR_NONE; + } - done = 1; - switch (imsg.hdr.type) { - case IMSG_CTL_OK: { - return 1; + for (i = 0; !header_done && cur == HDR_NONE && + i < (sizeof(keywords) / sizeof(keywords[0])); i++) + if (len > strlen(keywords[i].word) && + !strncasecmp(buf, keywords[i].word, + strlen(keywords[i].word))) + cur = keywords[i].type; + + if (cur != HDR_NONE) + header_seen = 1; + + if (cur != HDR_BCC) { + /* save data, \n -> \r\n, . -> .. */ + if (buf[len - 1] == '\n') + new_len = msg.len + len + 1; + else + new_len = msg.len + len + 2; + + if ((len == 1 && buf[0] == '.') || + (len > 1 && buf[0] == '.' && buf[1] == '\n')) { + dotonly = 1; + new_len++; + } else + dotonly = 0; + + if ((newp = realloc(msg.data, new_len)) == NULL) + err(1, "realloc header"); + msg.data = newp; + if (dotonly) + memcpy(msg.data + msg.len, twodots, 2); + else + memcpy(msg.data + msg.len, buf, len); + msg.len = new_len; + msg.data[msg.len - 2] = '\r'; + msg.data[msg.len - 1] = '\n'; } - case IMSG_CTL_FAIL: - return 0; - default: - errx(1, "unexpected reply (%d)", imsg.hdr.type); + + /* + * using From: as envelope sender is not sendmail compatible, + * but I really want it that way - maybe needs a knob + */ + if (cur == HDR_FROM) { + msg.saw_from++; + if (get_from) + parse_addr(buf, len, 1); } - imsg_free(&imsg); + + if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)) + parse_addr(buf, len, 0); + + if (cur == HDR_DATE) + msg.saw_date++; + if (cur == HDR_MSGID) + msg.saw_msgid++; } - return 1; + return (!header_seen); } -int -enqueue_write_message(FILE *fpin, FILE *fpout) +void +parse_addr(char *s, size_t len, int is_from) { - char *buf, *lbuf; - size_t len; - - lbuf = NULL; - while ((buf = fgetln(fpin, &len))) { - if (buf[len - 1] == '\n') { - buf[len - 1] = '\0'; - len--; + size_t pos = 0; + int terminal = 0; + + /* unless this is a continuation... */ + if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') { + /* ... skip over everything before the ':' */ + for (; pos < len && s[pos] != ':'; pos++) + ; /* nothing */ + /* ... and check & reset parser state */ + parse_addr_terminal(is_from); + } + + /* skip over ':' ',' ';' and whitespace */ + for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' || + s[pos] == ',' || s[pos] == ';'); pos++) + ; /* nothing */ + + for (; pos < len; pos++) { + if (!pstate.esc && !pstate.quote && s[pos] == '(') + pstate.comment++; + if (!pstate.comment && !pstate.esc && s[pos] == '"') + pstate.quote = !pstate.quote; + + if (!pstate.comment && !pstate.quote && !pstate.esc) { + if (s[pos] == ':') { /* group */ + for(pos++; pos < len && WSP(s[pos]); pos++) + ; /* nothing */ + pstate.wpos = 0; + } + if (s[pos] == '\n' || s[pos] == '\r') + break; + if (s[pos] == ',' || s[pos] == ';') { + terminal = 1; + break; + } + if (s[pos] == '<') { + pstate.brackets = 1; + pstate.wpos = 0; + } + if (pstate.brackets && s[pos] == '>') + terminal = 1; } - 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; + + if (!pstate.comment && !terminal && (!(!(pstate.quote || + pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) { + if (pstate.wpos >= sizeof(pstate.buf)) + errx(1, "address exceeds buffer size"); + pstate.buf[pstate.wpos++] = s[pos]; } - if (fprintf(fpout, "%s\n", buf) != (int)len + 1) - return 0; + + if (!pstate.quote && pstate.comment && s[pos] == ')') + pstate.comment--; + + if (!pstate.esc && !pstate.comment && !pstate.quote && + s[pos] == '\\') + pstate.esc = 1; + else + pstate.esc = 0; } - free(lbuf); - return 1; + + if (terminal) + parse_addr_terminal(is_from); + + for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++) + ; /* nothing */ + + if (pos < len) + parse_addr(s + pos, len - pos, is_from); +} + +void +parse_addr_terminal(int is_from) +{ + if (pstate.comment || pstate.quote || pstate.esc) + errx(1, "syntax error in address"); + if (pstate.wpos) { + if (pstate.wpos >= sizeof(pstate.buf)) + errx(1, "address exceeds buffer size"); + pstate.buf[pstate.wpos] = '\0'; + if (is_from) + msg.from = qualify_addr(pstate.buf); + else + rcpt_add(pstate.buf); + pstate.wpos = 0; + } +} + +char * +qualify_addr(char *in) +{ + char *out; + + if (strchr(in, '@') == NULL) { + if (asprintf(&out, "%s@%s", in, host) == -1) + err(1, "qualify asprintf"); + } else + if ((out = strdup(in)) == NULL) + err(1, "qualify strdup"); + + return (out); +} + +void +rcpt_add(char *addr) +{ + void *nrcpts; + + if ((nrcpts = realloc(msg.rcpts, + sizeof(char *) * (msg.rcpt_cnt + 1))) == NULL) + err(1, "rcpt_add realloc"); + msg.rcpts = nrcpts; + msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr); +} + +void +received(void) +{ + femail_put( + "Received: (from %s@%s, uid %lu)\r\n\tby %s\r\n\t%s\r\n", + user, "localhost", (u_long)getuid(), host, time_to_text(timestamp)); } int -enqueue_init(struct message *messagep) +open_connection(void) { - int done = 0; - int n; - struct imsg imsg; + struct imsg imsg; + int fd; + int n; + + imsg_compose(ibuf, IMSG_SMTP_ENQUEUE, 0, 0, -1, NULL, 0); - imsg_compose(ibuf, IMSG_QUEUE_CREATE_MESSAGE, 0, 0, -1, messagep, sizeof(*messagep)); while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) < 0) - err(1, "write error"); + err(1, "write error"); - while (!done) { + while (1) { if ((n = imsg_read(ibuf)) == -1) errx(1, "imsg_read error"); if (n == 0) @@ -257,110 +529,183 @@ enqueue_init(struct message *messagep) if ((n = imsg_get(ibuf, &imsg)) == -1) errx(1, "imsg_get error"); - if (n == 0) continue; - done = 1; - switch (imsg.hdr.type) { - case IMSG_CTL_OK: { - struct message *mp; - - mp = imsg.data; - messagep->session_id = mp->session_id; - strlcpy(messagep->message_id, mp->message_id, - sizeof(messagep->message_id)); - - return 1; - } - case IMSG_CTL_FAIL: - return 0; - default: - err(1, "unexpected reply (%d)", imsg.hdr.type); - } + fd = imsg_get_fd(ibuf, &imsg); imsg_free(&imsg); + + break; } - return 0; + return fd; } int -enqueue_messagefd(struct message *messagep) +read_reply(void) { - int done = 0; - int n; - struct imsg imsg; + char *lbuf = NULL; + size_t len, pos, spos; + long status = 0; + char buf[BUFSIZ]; + ssize_t rlen; + int done = 0; + + for (len = pos = spos = 0; !done;) { + if (pos == 0 || + (pos > 0 && memchr(buf + pos, '\n', len - pos) == NULL)) { + memmove(buf, buf + pos, len - pos); + len -= pos; + pos = 0; + if ((rlen = read(msg.fd, buf + len, + sizeof(buf) - len)) == -1) + err(1, "read"); + len += rlen; + } + spos = pos; - imsg_compose(ibuf, IMSG_QUEUE_MESSAGE_FILE, 0, 0, -1, messagep, sizeof(*messagep)); - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) - err(1, "write error"); + /* status code */ + for (; pos < len && buf[pos] >= '0' && buf[pos] <= '9'; pos++) + ; /* nothing */ - while (!done) { - if ((n = imsg_read(ibuf)) == -1) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); + if (pos == len) + return (0); - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); + if (buf[pos] == ' ') + done = 1; + else if (buf[pos] != '-') + errx(1, "invalid syntax in reply from server"); - if (n == 0) - continue; + /* skip up to \n */ + for (; pos < len && buf[pos - 1] != '\n'; pos++) + ; /* nothing */ - done = 1; - switch (imsg.hdr.type) { - case IMSG_CTL_OK: - return imsg_get_fd(ibuf, &imsg); - case IMSG_CTL_FAIL: - return -1; - default: - err(1, "unexpected reply (%d)", imsg.hdr.type); + if (verbose) { + size_t clen; + + clen = pos - spos + 1; /* + 1 for trailing \0 */ + if (buf[pos - 1] == '\n') + clen--; + if (buf[pos - 2] == '\r') + clen--; + if ((lbuf = malloc(clen)) == NULL) + err(1, NULL); + strlcpy(lbuf, buf + spos, clen); + printf("<<< %s\n", lbuf); + free(lbuf); } - imsg_free(&imsg); } - return -1; + status = strtol(buf, NULL, 10); + if (status < 100 || status > 999) + errx(1, "error reading status: out of range"); + + return (status); } +void +greeting(int use_ehlo) +{ + int status; + char *cmd, *how; + + if (use_ehlo) + how = "EHLO"; + else + how = "HELO"; + + if (asprintf(&cmd, "%s %s", how, host) == -1) + err(1, "asprintf"); + send_cmd(cmd); + free(cmd); + + if ((status = read_reply()) != STATUS_HELO) { + if (use_ehlo) + greeting(0); + else + errx(1, "remote host refuses our greeting"); + } +} -int -enqueue_commit(struct message *messagep) +void +mailfrom(char *addr) { - int done = 0; - int n; - struct imsg imsg; + int status; + char *cmd; - imsg_compose(ibuf, IMSG_QUEUE_COMMIT_MESSAGE, 0, 0, -1, messagep, sizeof(*messagep)); - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) - err(1, "write error"); + if (asprintf(&cmd, "MAIL FROM:<%s>", addr) == -1) + err(1, "asprintf"); + send_cmd(cmd); + free(cmd); - while (!done) { - if ((n = imsg_read(ibuf)) == -1) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); + if ((status = read_reply()) != STATUS_MAILFROM) + errx(1, "mail from %s refused by server", addr); +} - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); +void +rcptto(char *addr) +{ + int status; + char *cmd; - if (n == 0) - continue; + if (asprintf(&cmd, "RCPT TO:<%s>", addr) == -1) + err(1, "asprintf"); + send_cmd(cmd); + free(cmd); - done = 1; - switch (imsg.hdr.type) { - case IMSG_CTL_OK: { - return 1; - } - case IMSG_CTL_FAIL: { - return 0; - } - default: - err(1, "unexpected reply (%d)", imsg.hdr.type); - } - imsg_free(&imsg); + if ((status = read_reply()) != STATUS_RCPTTO) + errx(1, "rcpt to %s refused by server", addr); +} + +void +start_data(void) +{ + int status; + + send_cmd("DATA"); + + if ((status = read_reply()) != STATUS_DATA) + errx(1, "server sends error after DATA"); +} + +void +send_message(int noheader) +{ + /* our own headers */ + received(); + + if (!msg.saw_from) { + if (msg.fromname != NULL) + femail_put("From: %s <%s>\r\n", msg.fromname, msg.from); + else + femail_put("From: %s\r\n", msg.from); } - return 0; + if (!msg.saw_date) + femail_put("Date: %s\r\n", time_to_text(timestamp)); + + if (!msg.saw_msgid) + femail_put("Message-Id: <%llu.enqueue@%s>\r\n", + queue_generate_id(), host); + + if (noheader) + femail_write("\r\n", 2); + + femail_write(msg.data, msg.len); +} + +void +end_data(void) +{ + int status; + + femail_write(".\r\n", 3); + + if ((status = read_reply()) != STATUS_QUEUED) + errx(1, "error after sending mail, got status %d", status); + + send_cmd("QUIT"); + + if ((status = read_reply()) != STATUS_QUIT) + errx(1, "server sends error after QUIT"); } |