aboutsummaryrefslogtreecommitdiffstats
path: root/smtp_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtp_client.c')
-rw-r--r--smtp_client.c923
1 files changed, 0 insertions, 923 deletions
diff --git a/smtp_client.c b/smtp_client.c
deleted file mode 100644
index 8e146e1b..00000000
--- a/smtp_client.c
+++ /dev/null
@@ -1,923 +0,0 @@
-/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * 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.
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <resolv.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
-#include "ioev.h"
-#include "smtp.h"
-
-#define TRACE_SMTPCLT 2
-#define TRACE_IO 3
-
-enum {
- STATE_INIT = 0,
- STATE_BANNER,
- STATE_EHLO,
- STATE_HELO,
- STATE_LHLO,
- STATE_STARTTLS,
- STATE_AUTH,
- STATE_AUTH_PLAIN,
- STATE_AUTH_LOGIN,
- STATE_AUTH_LOGIN_USER,
- STATE_AUTH_LOGIN_PASS,
- STATE_READY,
- STATE_MAIL,
- STATE_RCPT,
- STATE_DATA,
- STATE_BODY,
- STATE_EOM,
- STATE_RSET,
- STATE_QUIT,
-
- STATE_LAST
-};
-
-#define base64_encode __b64_ntop
-#define base64_decode __b64_pton
-
-#define FLAG_TLS 0x01
-#define FLAG_TLS_VERIFIED 0x02
-
-#define SMTP_EXT_STARTTLS 0x01
-#define SMTP_EXT_PIPELINING 0x02
-#define SMTP_EXT_AUTH 0x04
-#define SMTP_EXT_AUTH_PLAIN 0x08
-#define SMTP_EXT_AUTH_LOGIN 0x10
-#define SMTP_EXT_DSN 0x20
-#define SMTP_EXT_SIZE 0x40
-
-struct smtp_client {
- void *tag;
- struct smtp_params params;
-
- int state;
- int flags;
- int ext;
- size_t ext_size;
-
- struct io *io;
- char *reply;
- size_t replysz;
-
- struct smtp_mail *mail;
- int rcptidx;
- int rcptok;
-};
-
-void log_trace_verbose(int);
-void log_trace(int, const char *, ...)
- __attribute__((format (printf, 2, 3)));
-
-static void smtp_client_io(struct io *, int, void *);
-static void smtp_client_free(struct smtp_client *);
-static void smtp_client_state(struct smtp_client *, int);
-static void smtp_client_abort(struct smtp_client *, int, const char *);
-static void smtp_client_cancel(struct smtp_client *, int, const char *);
-static void smtp_client_sendcmd(struct smtp_client *, char *, ...);
-static void smtp_client_sendbody(struct smtp_client *);
-static int smtp_client_readline(struct smtp_client *);
-static int smtp_client_replycat(struct smtp_client *, const char *);
-static void smtp_client_response(struct smtp_client *, const char *);
-static void smtp_client_mail_abort(struct smtp_client *);
-static void smtp_client_mail_status(struct smtp_client *, const char *);
-static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *);
-
-static const char *strstate[STATE_LAST] = {
- "INIT",
- "BANNER",
- "EHLO",
- "HELO",
- "LHLO",
- "STARTTLS",
- "AUTH",
- "AUTH_PLAIN",
- "AUTH_LOGIN",
- "AUTH_LOGIN_USER",
- "AUTH_LOGIN_PASS",
- "READY",
- "MAIL",
- "RCPT",
- "DATA",
- "BODY",
- "EOM",
- "RSET",
- "QUIT",
-};
-
-struct smtp_client *
-smtp_connect(const struct smtp_params *params, void *tag)
-{
- struct smtp_client *proto;
-
- proto = calloc(1, sizeof *proto);
- if (proto == NULL)
- return NULL;
-
- memmove(&proto->params, params, sizeof(*params));
- proto->tag = tag;
- proto->io = io_new();
- if (proto->io == NULL) {
- free(proto);
- return NULL;
- }
- io_set_callback(proto->io, smtp_client_io, proto);
- io_set_timeout(proto->io, proto->params.timeout);
-
- if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) {
- smtp_client_abort(proto, FAIL_CONN, io_error(proto->io));
- return NULL;
- }
-
- return proto;
-}
-
-void
-smtp_cert_verified(struct smtp_client *proto, int verified)
-{
- if (verified == CERT_OK)
- proto->flags |= FLAG_TLS_VERIFIED;
-
- else if (proto->params.tls_verify) {
- errno = EAUTH;
- smtp_client_abort(proto, FAIL_CONN,
- "Invalid server certificate");
- return;
- }
-
- io_resume(proto->io, IO_IN);
-
- if (proto->state == STATE_INIT)
- smtp_client_state(proto, STATE_BANNER);
- else {
- /* Clear extensions before re-issueing an EHLO command. */
- proto->ext = 0;
- smtp_client_state(proto, STATE_EHLO);
- }
-}
-
-void
-smtp_set_tls(struct smtp_client *proto, void *ctx)
-{
- io_start_tls(proto->io, ctx);
-}
-
-void
-smtp_quit(struct smtp_client *proto)
-{
- if (proto->state != STATE_READY)
- fatalx("connection is not ready");
-
- smtp_client_state(proto, STATE_QUIT);
-}
-
-void
-smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail)
-{
- if (proto->state != STATE_READY)
- fatalx("connection is not ready");
-
- proto->mail = mail;
- smtp_client_state(proto, STATE_MAIL);
-}
-
-static void
-smtp_client_free(struct smtp_client *proto)
-{
- if (proto->mail)
- fatalx("current task should have been deleted already");
-
- smtp_closed(proto->tag, proto);
-
- if (proto->io)
- io_free(proto->io);
-
- free(proto->reply);
- free(proto);
-}
-
-/*
- * End the session immediatly.
- */
-static void
-smtp_client_abort(struct smtp_client *proto, int err, const char *reason)
-{
- smtp_failed(proto->tag, proto, err, reason);
-
- if (proto->mail)
- smtp_client_mail_abort(proto);
-
- smtp_client_free(proto);
-}
-
-/*
- * Properly close the session.
- */
-static void
-smtp_client_cancel(struct smtp_client *proto, int err, const char *reason)
-{
- if (proto->mail)
- fatal("not supposed to have a mail");
-
- smtp_failed(proto->tag, proto, err, reason);
-
- smtp_client_state(proto, STATE_QUIT);
-}
-
-static void
-smtp_client_state(struct smtp_client *proto, int newstate)
-{
- struct smtp_rcpt *rcpt;
- char ibuf[LINE_MAX], obuf[LINE_MAX];
- size_t n;
- int oldstate;
-
- if (proto->reply)
- proto->reply[0] = '\0';
-
- again:
- oldstate = proto->state;
- proto->state = newstate;
-
- log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto,
- strstate[oldstate],
- strstate[newstate]);
-
- /* don't try this at home! */
-#define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0)
-
- switch (proto->state) {
- case STATE_BANNER:
- io_set_read(proto->io);
- break;
-
- case STATE_EHLO:
- smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo);
- break;
-
- case STATE_HELO:
- smtp_client_sendcmd(proto, "HELO %s", proto->params.helo);
- break;
-
- case STATE_LHLO:
- smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo);
- break;
-
- case STATE_STARTTLS:
- if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS)
- smtp_client_state(proto, STATE_AUTH);
- else if (proto->ext & SMTP_EXT_STARTTLS)
- smtp_client_sendcmd(proto, "STARTTLS");
- else if (proto->params.tls_req == TLS_FORCE)
- smtp_client_cancel(proto, FAIL_IMPL,
- "TLS not supported by remote host");
- else
- smtp_client_state(proto, STATE_AUTH);
- break;
-
- case STATE_AUTH:
- if (!proto->params.auth_user)
- smtp_client_state(proto, STATE_READY);
- else if ((proto->flags & FLAG_TLS) == 0)
- smtp_client_cancel(proto, FAIL_IMPL,
- "Authentication requires TLS");
- else if ((proto->ext & SMTP_EXT_AUTH) == 0)
- smtp_client_cancel(proto, FAIL_IMPL,
- "AUTH not supported by remote host");
- else if (proto->ext & SMTP_EXT_AUTH_PLAIN)
- smtp_client_state(proto, STATE_AUTH_PLAIN);
- else if (proto->ext & SMTP_EXT_AUTH_LOGIN)
- smtp_client_state(proto, STATE_AUTH_LOGIN);
- else
- smtp_client_cancel(proto, FAIL_IMPL,
- "No supported AUTH method");
- break;
-
- case STATE_AUTH_PLAIN:
- (void)strlcpy(ibuf, "-", sizeof(ibuf));
- (void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf));
- if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf));
- if (n >= sizeof(ibuf)) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- *strchr(ibuf, ':') = '\0';
- ibuf[0] = '\0';
- if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf);
- explicit_bzero(ibuf, sizeof ibuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_AUTH_LOGIN:
- smtp_client_sendcmd(proto, "AUTH LOGIN");
- break;
-
- case STATE_AUTH_LOGIN_USER:
- if (base64_encode(proto->params.auth_user,
- strlen(proto->params.auth_user), obuf,
- sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "%s", obuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_AUTH_LOGIN_PASS:
- if (base64_encode(proto->params.auth_pass,
- strlen(proto->params.auth_pass), obuf,
- sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "%s", obuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_READY:
- smtp_ready(proto->tag, proto);
- break;
-
- case STATE_MAIL:
- if (proto->ext & SMTP_EXT_DSN)
- smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s",
- proto->mail->from,
- proto->mail->dsn_ret ? " RET=" : "",
- proto->mail->dsn_ret ? proto->mail->dsn_ret : "",
- proto->mail->dsn_envid ? " ENVID=" : "",
- proto->mail->dsn_envid ? proto->mail->dsn_envid : "");
- else
- smtp_client_sendcmd(proto, "MAIL FROM:<%s>",
- proto->mail->from);
- break;
-
- case STATE_RCPT:
- if (proto->rcptidx == proto->mail->rcptcount) {
- smtp_client_state(proto, STATE_DATA);
- break;
- }
- rcpt = &proto->mail->rcpt[proto->rcptidx];
- if (proto->ext & SMTP_EXT_DSN)
- smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s",
- rcpt->to,
- rcpt->dsn_notify ? " NOTIFY=" : "",
- rcpt->dsn_notify ? rcpt->dsn_notify : "",
- rcpt->dsn_orcpt ? " ORCPT=" : "",
- rcpt->dsn_orcpt ? rcpt->dsn_orcpt : "");
- else
- smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to);
- break;
-
- case STATE_DATA:
- if (proto->rcptok == 0) {
- smtp_client_mail_abort(proto);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_sendcmd(proto, "DATA");
- break;
-
- case STATE_BODY:
- fseek(proto->mail->fp, 0, SEEK_SET);
- smtp_client_sendbody(proto);
- break;
-
- case STATE_EOM:
- smtp_client_sendcmd(proto, ".");
- break;
-
- case STATE_RSET:
- smtp_client_sendcmd(proto, "RSET");
- break;
-
- case STATE_QUIT:
- smtp_client_sendcmd(proto, "QUIT");
- break;
-
- default:
- fatalx("%s: bad state %d", __func__, proto->state);
- }
-#undef smtp_client_state
-}
-
-/*
- * Handle a response to an SMTP command
- */
-static void
-smtp_client_response(struct smtp_client *proto, const char *line)
-{
- struct smtp_rcpt *rcpt;
- int i, seen;
-
- switch (proto->state) {
- case STATE_BANNER:
- if (line[0] != '2')
- smtp_client_abort(proto, FAIL_RESP, line);
- else if (proto->params.lmtp)
- smtp_client_state(proto, STATE_LHLO);
- else
- smtp_client_state(proto, STATE_EHLO);
- break;
-
- case STATE_EHLO:
- if (line[0] != '2') {
- /*
- * Either rejected or not implemented. If we want to
- * use EHLO extensions, report an SMTP error.
- * Otherwise, fallback to using HELO.
- */
- if ((proto->params.tls_req == TLS_FORCE) ||
- (proto->params.auth_user))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_HELO);
- break;
- }
- smtp_client_state(proto, STATE_STARTTLS);
- break;
-
- case STATE_HELO:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_LHLO:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_STARTTLS:
- if (line[0] != '2') {
- if ((proto->params.tls_req == TLS_FORCE) ||
- (proto->params.auth_user)) {
- smtp_client_cancel(proto, FAIL_RESP, line);
- break;
- }
- smtp_client_state(proto, STATE_AUTH);
- }
- else
- smtp_require_tls(proto->tag, proto);
- break;
-
- case STATE_AUTH_PLAIN:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_AUTH_LOGIN:
- if (strncmp(line, "334 ", 4))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_AUTH_LOGIN_USER);
- break;
-
- case STATE_AUTH_LOGIN_USER:
- if (strncmp(line, "334 ", 4))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_AUTH_LOGIN_PASS);
- break;
-
- case STATE_AUTH_LOGIN_PASS:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_MAIL:
- if (line[0] != '2') {
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_state(proto, STATE_RCPT);
- break;
-
- case STATE_RCPT:
- rcpt = &proto->mail->rcpt[proto->rcptidx++];
- if (line[0] != '2')
- smtp_client_rcpt_status(proto, rcpt, line);
- else {
- proto->rcptok++;
- smtp_client_state(proto, STATE_RCPT);
- }
- break;
-
- case STATE_DATA:
- if (line[0] != '2' && line[0] != '3') {
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_state(proto, STATE_BODY);
- break;
-
- case STATE_EOM:
- if (proto->params.lmtp) {
- /*
- * LMTP reports a status of each accepted RCPT.
- * Report status for the first pending RCPT and read
- * more lines if another rcpt needs a status.
- */
- for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) {
- rcpt = &proto->mail->rcpt[i];
- if (rcpt->done)
- continue;
- if (seen) {
- io_set_read(proto->io);
- return;
- }
- smtp_client_rcpt_status(proto,
- &proto->mail->rcpt[i], line);
- seen = 1;
- }
- }
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_RSET:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_QUIT:
- smtp_client_free(proto);
- break;
-
- default:
- fatalx("%s: bad state %d", __func__, proto->state);
- }
-}
-
-static void
-smtp_client_io(struct io *io, int evt, void *arg)
-{
- struct smtp_client *proto = arg;
-
- log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io));
-
- switch (evt) {
- case IO_CONNECTED:
- if (proto->params.tls_req == TLS_SMTPS) {
- io_set_write(io);
- smtp_require_tls(proto->tag, proto);
- }
- else
- smtp_client_state(proto, STATE_BANNER);
- break;
-
- case IO_TLSREADY:
- proto->flags |= FLAG_TLS;
- io_pause(proto->io, IO_IN);
- smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io));
- break;
-
- case IO_DATAIN:
- while (smtp_client_readline(proto))
- ;
- break;
-
- case IO_LOWAT:
- if (proto->state == STATE_BODY)
- smtp_client_sendbody(proto);
- else
- io_set_read(io);
- break;
-
- case IO_TIMEOUT:
- errno = ETIMEDOUT;
- smtp_client_abort(proto, FAIL_CONN, "Connection timeout");
- break;
-
- case IO_ERROR:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- case IO_TLSERROR:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- case IO_DISCONNECTED:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- default:
- fatalx("%s: bad event %d", __func__, evt);
- }
-}
-
-/*
- * return 1 if a new line is expected.
- */
-static int
-smtp_client_readline(struct smtp_client *proto)
-{
- const char *e;
- size_t len;
- char *line, *msg, *p;
- int cont;
-
- line = io_getline(proto->io, &len);
- if (line == NULL) {
- if (io_datalen(proto->io) >= proto->params.linemax)
- smtp_client_abort(proto, FAIL_PROTO, "Line too long");
- return 0;
- }
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line);
-
- /* Validate SMTP */
- if (len > 3) {
- msg = line + 4;
- cont = (line[3] == '-');
- } else if (len == 3) {
- msg = line + 3;
- cont = 0;
- } else {
- smtp_client_abort(proto, FAIL_PROTO, "Response too short");
- return 0;
- }
-
- /* Validate reply code. */
- if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
- !isdigit((unsigned char)line[2])) {
- smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code");
- return 0;
- }
-
- /* Validate reply message. */
- for (p = msg; *p; p++)
- if (!isprint((unsigned char)*p)) {
- smtp_client_abort(proto, FAIL_PROTO,
- "Non-printable characters in response");
- return 0;
- }
-
- /* Read extensions. */
- if (proto->state == STATE_EHLO) {
- if (strcmp(msg, "STARTTLS") == 0)
- proto->ext |= SMTP_EXT_STARTTLS;
- else if (strncmp(msg, "AUTH ", 5) == 0) {
- proto->ext |= SMTP_EXT_AUTH;
- if ((p = strstr(msg, " PLAIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- proto->ext |= SMTP_EXT_AUTH_PLAIN;
- if ((p = strstr(msg, " LOGIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- proto->ext |= SMTP_EXT_AUTH_LOGIN;
- }
- else if (strcmp(msg, "PIPELINING") == 0)
- proto->ext |= SMTP_EXT_PIPELINING;
- else if (strcmp(msg, "DSN") == 0)
- proto->ext |= SMTP_EXT_DSN;
- else if (strncmp(msg, "SIZE ", 5) == 0) {
- proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e);
- if (e == NULL)
- proto->ext |= SMTP_EXT_SIZE;
- }
- }
-
- if (smtp_client_replycat(proto, line) == -1) {
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return 0;
- }
-
- if (cont)
- return 1;
-
- if (io_datalen(proto->io)) {
- /*
- * There should be no pending data after a response is read,
- * except for the multiple status lines after a LMTP message.
- * It can also happen with pipelineing, but we don't do that
- * for now.
- */
- if (!(proto->params.lmtp && proto->state == STATE_EOM)) {
- smtp_client_abort(proto, FAIL_PROTO, "Trailing data");
- return 0;
- }
- }
-
- io_set_write(proto->io);
- smtp_client_response(proto, proto->reply);
- return 0;
-}
-
-/*
- * Concatenate the given response line.
- */
-static int
-smtp_client_replycat(struct smtp_client *proto, const char *line)
-{
- size_t len;
- char *tmp;
- int first;
-
- if (proto->reply && proto->reply[0]) {
- /*
- * If the line is the continuation of an multi-line response,
- * skip the status and ESC parts. First, skip the status, then
- * skip the separator amd ESC if found.
- */
- first = 0;
- line += 3;
- if (line[0]) {
- line += 1;
- if (isdigit((unsigned char)line[0]) && line[1] == '.' &&
- isdigit((unsigned char)line[2]) && line[3] == '.' &&
- isdigit((unsigned char)line[4]) &&
- isspace((unsigned char)line[5]))
- line += 5;
- }
- } else
- first = 1;
-
- if (proto->reply) {
- len = strlcat(proto->reply, line, proto->replysz);
- if (len < proto->replysz)
- return 0;
- }
- else
- len = strlen(line);
-
- if (len > proto->params.ibufmax) {
- errno = EMSGSIZE;
- return -1;
- }
-
- /* Allocate by multiples of 2^8 */
- len += (len % 256) ? (256 - (len % 256)) : 0;
-
- tmp = realloc(proto->reply, len);
- if (tmp == NULL)
- return -1;
- if (proto->reply == NULL)
- tmp[0] = '\0';
-
- proto->reply = tmp;
- proto->replysz = len;
- (void)strlcat(proto->reply, line, proto->replysz);
-
- /* Replace the separator with a space for the first line. */
- if (first && proto->reply[3])
- proto->reply[3] = ' ';
-
- return 0;
-}
-
-static void
-smtp_client_sendbody(struct smtp_client *proto)
-{
- ssize_t len;
- size_t sz = 0, total, w;
- char *ln = NULL;
- int n;
-
- total = io_queued(proto->io);
- w = 0;
-
- while (total < proto->params.obufmax) {
- if ((len = getline(&ln, &sz, proto->mail->fp)) == -1)
- break;
- if (ln[len - 1] == '\n')
- ln[len - 1] = '\0';
- n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln);
- if (n == -1) {
- free(ln);
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return;
- }
- total += n;
- w += n;
- }
- free(ln);
-
- if (ferror(proto->mail->fp)) {
- smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message");
- return;
- }
-
- log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w);
-
- if (feof(proto->mail->fp))
- smtp_client_state(proto, STATE_EOM);
-}
-
-static void
-smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...)
-{
- va_list ap;
- char *p;
- int len;
-
- va_start(ap, fmt);
- len = vasprintf(&p, fmt, ap);
- va_end(ap);
-
- if (len == -1) {
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return;
- }
-
- log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p);
-
- len = io_printf(proto->io, "%s\r\n", p);
- free(p);
-
- if (len == -1)
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
-}
-
-static void
-smtp_client_mail_status(struct smtp_client *proto, const char *status)
-{
- int i;
-
- for (i = 0; i < proto->mail->rcptcount; i++)
- smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status);
-
- smtp_done(proto->tag, proto, proto->mail);
- proto->mail = NULL;
-}
-
-static void
-smtp_client_mail_abort(struct smtp_client *proto)
-{
- smtp_done(proto->tag, proto, proto->mail);
- proto->mail = NULL;
-}
-
-static void
-smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line)
-{
- struct smtp_status status;
-
- if (rcpt->done)
- return;
-
- rcpt->done = 1;
- status.rcpt = rcpt;
- status.cmd = strstate[proto->state];
- status.status = line;
- smtp_status(proto->tag, proto, &status);
-}