#include <sys_defs.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
#include <iostuff.h>
#include <split_at.h>
#include <name_code.h>
#include <mail_params.h>
#include <smtp_stream.h>
#include <mail_queue.h>
#include <recipient_list.h>
#include <deliver_request.h>
#include <defer.h>
#include <bounce.h>
#include <record.h>
#include <rec_type.h>
#include <off_cvt.h>
#include <mark_corrupt.h>
#include <quote_821_local.h>
#include <mail_proto.h>
#include <mime_state.h>
#include <pfixtls.h>
#include "smtp.h"
#include "smtp_sasl.h"
#define SMTP_STATE_XFORWARD_NAME_ADDR 0
#define SMTP_STATE_XFORWARD_PROTO_HELO 1
#define SMTP_STATE_MAIL 2
#define SMTP_STATE_RCPT 3
#define SMTP_STATE_DATA 4
#define SMTP_STATE_DOT 5
#define SMTP_STATE_ABORT 6
#define SMTP_STATE_QUIT 7
#define SMTP_STATE_LAST 8
int *xfer_timeouts[SMTP_STATE_LAST] = {
&var_smtp_xfwd_tmout,
&var_smtp_xfwd_tmout,
&var_smtp_mail_tmout,
&var_smtp_rcpt_tmout,
&var_smtp_data0_tmout,
&var_smtp_data2_tmout,
&var_smtp_rset_tmout,
&var_smtp_quit_tmout,
};
char *xfer_states[SMTP_STATE_LAST] = {
"sending XFORWARD name/address",
"sending XFORWARD protocol/helo_name",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
"sending end of data -- message may be sent more than once",
"sending final RSET",
"sending QUIT",
};
char *xfer_request[SMTP_STATE_LAST] = {
"XFORWARD name/address command",
"XFORWARD helo/protocol command",
"MAIL FROM command",
"RCPT TO command",
"DATA command",
"end of DATA command",
"final RSET command",
"QUIT command",
};
int smtp_helo(SMTP_STATE *state, int misc_flags)
{
SMTP_SESSION *session = state->session;
DELIVER_REQUEST *request = state->request;
SMTP_RESP *resp;
int except;
char *lines;
char *words;
char *word;
int n;
static NAME_CODE xforward_features[] = {
XFORWARD_NAME, SMTP_FEATURE_XFORWARD_NAME,
XFORWARD_ADDR, SMTP_FEATURE_XFORWARD_ADDR,
XFORWARD_PROTO, SMTP_FEATURE_XFORWARD_PROTO,
XFORWARD_HELO, SMTP_FEATURE_XFORWARD_HELO,
0, 0,
};
int oldfeatures;
int rval;
smtp_timeout_setup(state->session->stream, var_smtp_helo_tmout);
if ((except = vstream_setjmp(state->session->stream)) != 0)
return (smtp_stream_except(state, except,
"receiving the initial SMTP greeting"));
switch ((resp = smtp_chat_resp(state))->code / 100) {
case 2:
break;
case 5:
if (var_smtp_skip_5xx_greeting)
resp->code = 400;
default:
return (smtp_site_fail(state, resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
}
if (resp->str[strspn(resp->str, "20 *\t\n")] == 0)
state->features |= SMTP_FEATURE_MAYBEPIX;
words = resp->str;
(void) mystrtok(&words, "- \t\n");
for (n = 0; (word = mystrtok(&words, " \t\n")) != 0; n++) {
if (n == 0 && strcasecmp(word, var_myhostname) == 0) {
if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
msg_warn("host %s greeted me with my own hostname %s",
session->namaddr, var_myhostname);
} else if (strcasecmp(word, "ESMTP") == 0)
state->features |= SMTP_FEATURE_ESMTP;
}
if (var_smtp_always_ehlo && (state->features & SMTP_FEATURE_MAYBEPIX) == 0)
state->features |= SMTP_FEATURE_ESMTP;
if (var_smtp_never_ehlo || (state->features & SMTP_FEATURE_MAYBEPIX) != 0)
state->features &= ~SMTP_FEATURE_ESMTP;
if (state->features & SMTP_FEATURE_ESMTP) {
smtp_chat_cmd(state, "EHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(state))->code / 100 != 2)
state->features &= ~SMTP_FEATURE_ESMTP;
}
if ((state->features & SMTP_FEATURE_ESMTP) == 0) {
smtp_chat_cmd(state, "HELO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(state))->code / 100 != 2)
return (smtp_site_fail(state, resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
return (0);
}
if (var_smtp_always_ehlo)
state->features |= SMTP_FEATURE_ESMTP;
lines = resp->str;
oldfeatures = state->features;
while ((words = mystrtok(&lines, "\n")) != 0) {
if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "XFORWARD") == 0)
while ((word = mystrtok(&words, " \t")) != 0)
state->features |= name_code(xforward_features,
NAME_CODE_FLAG_NONE, word);
else if (strcasecmp(word, "SIZE") == 0) {
state->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
if (!alldig(word))
msg_warn("bad size limit \"%s\" in EHLO reply from %s",
word, session->namaddr);
else
state->size_limit = off_cvt_string(word);
}
}
else if (strcasecmp(word, "STARTTLS") == 0)
state->features |= SMTP_FEATURE_STARTTLS;
#ifdef USE_SASL_AUTH
else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
smtp_sasl_helo_auth(state, words);
#endif
else if (strcasecmp(word, var_myhostname) == 0) {
if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) {
msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
session->namaddr, var_myhostname);
return (smtp_site_fail(state, session->best ? 550 : 450,
"mail for %s loops back to myself",
request->nexthop));
}
}
}
}
if (msg_verbose)
msg_info("server features: 0x%x size %.0f",
state->features, (double) state->size_limit);
#ifdef USE_SSL
if ((state->features & SMTP_FEATURE_STARTTLS) &&
(var_smtp_tls_note_starttls_offer) &&
(!(session->tls_enforce_tls || session->tls_use_tls)))
msg_info("Host offered STARTTLS: [%s]", session->host);
if ((session->tls_enforce_tls) &&
!(state->features & SMTP_FEATURE_STARTTLS))
{
return (smtp_site_fail(state, 450, "Could not start TLS: not offered"));
}
if ((session->tls_enforce_tls) && !pfixtls_clientengine) {
return (smtp_site_fail(state, 450,
"Could not start TLS: our TLS-engine not running"));
}
if ((state->features & SMTP_FEATURE_STARTTLS) &&
((session->tls_use_tls && pfixtls_clientengine) ||
(session->tls_enforce_tls))) {
smtp_chat_cmd(state, "STARTTLS");
if ((resp = smtp_chat_resp(state))->code / 100 != 2) {
state->features &= ~SMTP_FEATURE_STARTTLS;
if (session->tls_enforce_tls)
return (smtp_site_fail(state, resp->code,
"host %s refused to start TLS: %s",
session->host,
translit(resp->str, "\n", " ")));
} else {
if (rval = pfixtls_start_clienttls(session->stream,
var_smtp_starttls_tmout,
session->tls_enforce_peername,
session->host,
&(session->tls_info)))
return (smtp_site_fail(state, 450,
"Could not start TLS: client failure"));
if (session->tls_info.peer_CN != NULL) {
if (!session->tls_info.peer_verified) {
msg_info("Peer certficate could not be verified");
if (session->tls_enforce_tls) {
pfixtls_stop_clienttls(session->stream,
var_smtp_starttls_tmout, 1,
&(session->tls_info));
return(smtp_site_fail(state, 450, "TLS-failure: Could not verify certificate"));
}
}
} else if (session->tls_enforce_tls) {
pfixtls_stop_clienttls(session->stream,
var_smtp_starttls_tmout, 1,
&(session->tls_info));
return (smtp_site_fail(state, 450, "TLS-failure: Cannot verify hostname"));
}
state->features = oldfeatures;
#ifdef USE_SASL_AUTH
if (state->sasl_mechanism_list) {
myfree(state->sasl_mechanism_list);
state->sasl_mechanism_list = 0;
}
#endif
if (state->features & SMTP_FEATURE_ESMTP) {
smtp_chat_cmd(state, "EHLO %s", var_myhostname);
if ((resp = smtp_chat_resp(state))->code / 100 != 2)
state->features &= ~SMTP_FEATURE_ESMTP;
}
lines = resp->str;
(void) mystrtok(&lines, "\n");
while ((words = mystrtok(&lines, "\n")) != 0) {
if (mystrtok(&words, "- ") &&
(word = mystrtok(&words, " \t=")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "SIZE") == 0)
state->features |= SMTP_FEATURE_SIZE;
else if (strcasecmp(word, "STARTTLS") == 0)
state->features |= SMTP_FEATURE_STARTTLS;
#ifdef USE_SASL_AUTH
else if (var_smtp_sasl_enable &&
strcasecmp(word, "AUTH") == 0)
smtp_sasl_helo_auth(state, words);
#endif
}
}
}
}
#endif
#ifdef USE_SASL_AUTH
if (var_smtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH))
return (smtp_sasl_helo_login(state));
#endif
return (0);
}
static void smtp_text_out(void *context, int rec_type,
const char *text, int len,
off_t unused_offset)
{
SMTP_STATE *state = (SMTP_STATE *) context;
SMTP_SESSION *session = state->session;
int data_left;
const char *data_start;
data_left = len;
data_start = text;
do {
if (state->space_left == var_smtp_line_limit
&& data_left > 0 && *data_start == '.')
smtp_fputc('.', session->stream);
if (var_smtp_line_limit > 0 && data_left >= state->space_left) {
smtp_fputs(data_start, state->space_left, session->stream);
data_start += state->space_left;
data_left -= state->space_left;
state->space_left = var_smtp_line_limit;
if (data_left > 0 || rec_type == REC_TYPE_CONT) {
smtp_fputc(' ', session->stream);
state->space_left -= 1;
}
} else {
if (rec_type == REC_TYPE_CONT) {
smtp_fwrite(data_start, data_left, session->stream);
state->space_left -= data_left;
} else {
smtp_fputs(data_start, data_left, session->stream);
state->space_left = var_smtp_line_limit;
}
break;
}
} while (data_left > 0);
}
static void smtp_header_out(void *context, int unused_header_class,
HEADER_OPTS *unused_info, VSTRING *buf,
off_t offset)
{
char *start = vstring_str(buf);
char *line;
char *next_line;
for (line = start; line; line = next_line) {
next_line = split_at(line, '\n');
smtp_text_out(context, REC_TYPE_NORM, line, next_line ?
next_line - line - 1 : strlen(line), offset);
}
}
int smtp_xfer(SMTP_STATE *state)
{
char *myname = "smtp_xfer";
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
SMTP_RESP *resp;
RECIPIENT *rcpt;
VSTRING *next_command = vstring_alloc(100);
NOCLOBBER int next_state;
NOCLOBBER int next_rcpt;
NOCLOBBER int send_state;
NOCLOBBER int recv_state;
NOCLOBBER int send_rcpt;
NOCLOBBER int recv_rcpt;
NOCLOBBER int nrcpt;
int except;
int rec_type;
NOCLOBBER int prev_type = 0;
int sndbufsize = 0;
NOCLOBBER int sndbuffree;
SOCKOPT_SIZE optlen = sizeof(sndbufsize);
NOCLOBBER int mail_from_rejected;
NOCLOBBER int downgrading;
int mime_errs;
int send_name_addr;
NOCLOBBER int send_proto_helo;
#define REWRITE_ADDRESS(dst, mid, src) do { \
if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(mid, src); \
smtp_unalias_addr(dst, vstring_str(mid)); \
} else { \
vstring_strcpy(dst, src); \
} \
} while (0)
#define QUOTE_ADDRESS(dst, src) do { \
if (*(src) && var_smtp_quote_821_env) { \
quote_821_local(dst, src); \
} else { \
vstring_strcpy(dst, src); \
} \
} while (0)
#define RETURN(x) do { \
vstring_free(next_command); \
if (state->mime_state) \
state->mime_state = mime_state_free(state->mime_state); \
return (x); \
} while (0)
#define SENDER_IS_AHEAD \
(recv_state < send_state || recv_rcpt != send_rcpt)
#define SENDER_IN_WAIT_STATE \
(send_state == SMTP_STATE_DOT || send_state == SMTP_STATE_LAST)
#define SENDING_MAIL \
(recv_state <= SMTP_STATE_DOT)
if (SMTP_RCPT_LEFT(state) <= 0)
msg_panic("smtp_xfer: bad recipient count: %d",
SMTP_RCPT_LEFT(state));
if (SMTP_RCPT_ISMARKED(request->rcpt_list.info))
msg_panic("smtp_xfer: bad recipient status: %d",
request->rcpt_list.info->status);
if (state->size_limit > 0 && state->size_limit < request->data_size) {
smtp_mesg_fail(state, 552,
"message size %lu exceeds size limit %.0f of server %s",
request->data_size, (double) state->size_limit,
session->namaddr);
RETURN(0);
}
if (state->features & SMTP_FEATURE_PIPELINING) {
if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET,
SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0)
msg_fatal("%s: getsockopt: %m", myname);
if (sndbufsize > VSTREAM_BUFSIZE)
sndbufsize = VSTREAM_BUFSIZE;
if (sndbufsize == 0) {
sndbufsize = VSTREAM_BUFSIZE;
if (setsockopt(vstream_fileno(state->session->stream), SOL_SOCKET,
SO_SNDBUF, (char *) &sndbufsize, optlen) < 0)
msg_fatal("%s: setsockopt: %m", myname);
}
if (msg_verbose)
msg_info("Using ESMTP PIPELINING, TCP send buffer size is %d",
sndbufsize);
} else {
sndbufsize = 0;
}
sndbuffree = sndbufsize;
nrcpt = 0;
send_name_addr =
var_smtp_send_xforward
&& (((state->features & SMTP_FEATURE_XFORWARD_NAME)
&& DEL_REQ_ATTR_AVAIL(request->client_name))
|| ((state->features & SMTP_FEATURE_XFORWARD_ADDR)
&& DEL_REQ_ATTR_AVAIL(request->client_addr)));
send_proto_helo =
var_smtp_send_xforward
&& (((state->features & SMTP_FEATURE_XFORWARD_PROTO)
&& DEL_REQ_ATTR_AVAIL(request->client_proto))
|| ((state->features & SMTP_FEATURE_XFORWARD_HELO)
&& DEL_REQ_ATTR_AVAIL(request->client_helo)));
if (send_name_addr)
recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
else if (send_proto_helo)
recv_state = send_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
mail_from_rejected = 0;
while (recv_state != SMTP_STATE_LAST) {
switch (send_state) {
default:
msg_panic("%s: bad sender state %d", myname, send_state);
case SMTP_STATE_XFORWARD_NAME_ADDR:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & SMTP_FEATURE_XFORWARD_NAME)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
request->client_name : XFORWARD_UNAVAILABLE);
if (state->features & SMTP_FEATURE_XFORWARD_ADDR)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
request->client_addr : XFORWARD_UNAVAILABLE);
if (send_proto_helo)
next_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
next_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_XFORWARD_PROTO_HELO:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & SMTP_FEATURE_XFORWARD_PROTO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
request->client_proto : XFORWARD_UNAVAILABLE);
if (state->features & SMTP_FEATURE_XFORWARD_HELO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
request->client_helo : XFORWARD_UNAVAILABLE);
next_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_MAIL:
QUOTE_ADDRESS(state->scratch, request->sender);
vstring_sprintf(next_command, "MAIL FROM:<%s>",
vstring_str(state->scratch));
if (state->features & SMTP_FEATURE_SIZE)
vstring_sprintf_append(next_command, " SIZE=%lu",
request->data_size);
if (state->features & SMTP_FEATURE_8BITMIME) {
if (strcmp(request->encoding, MAIL_ATTR_ENC_8BIT) == 0)
vstring_strcat(next_command, " BODY=8BITMIME");
else if (strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) == 0)
vstring_strcat(next_command, " BODY=7BIT");
else if (strcmp(request->encoding, MAIL_ATTR_ENC_NONE) != 0)
msg_warn("%s: unknown content encoding: %s",
request->queue_id, request->encoding);
}
#ifdef USE_SASL_AUTH
if (var_smtp_sasl_enable
&& (state->features & SMTP_FEATURE_AUTH)
&& state->sasl_passwd)
vstring_strcat(next_command, " AUTH=<>");
#endif
next_state = SMTP_STATE_RCPT;
break;
case SMTP_STATE_RCPT:
rcpt = request->rcpt_list.info + send_rcpt;
QUOTE_ADDRESS(state->scratch, rcpt->address);
vstring_sprintf(next_command, "RCPT TO:<%s>",
vstring_str(state->scratch));
if ((next_rcpt = send_rcpt + 1) == SMTP_RCPT_LEFT(state))
next_state = DEL_REQ_TRACE_ONLY(request->flags) ?
SMTP_STATE_ABORT : SMTP_STATE_DATA;
break;
case SMTP_STATE_DATA:
vstring_strcpy(next_command, "DATA");
next_state = SMTP_STATE_DOT;
break;
case SMTP_STATE_DOT:
vstring_strcpy(next_command, ".");
next_state = SMTP_STATE_QUIT;
break;
case SMTP_STATE_ABORT:
vstring_strcpy(next_command, "RSET");
next_state = SMTP_STATE_QUIT;
break;
case SMTP_STATE_QUIT:
vstring_strcpy(next_command, "QUIT");
next_state = SMTP_STATE_LAST;
break;
case SMTP_STATE_LAST:
VSTRING_RESET(next_command);
break;
}
VSTRING_TERMINATE(next_command);
if (SENDER_IN_WAIT_STATE
|| (SENDER_IS_AHEAD
&& (VSTRING_LEN(next_command) + 2 > sndbuffree
|| time((time_t *) 0) - vstream_ftime(session->stream) > 10))) {
while (SENDER_IS_AHEAD) {
if (recv_state < SMTP_STATE_XFORWARD_NAME_ADDR
|| recv_state > SMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
smtp_timeout_setup(state->session->stream,
*xfer_timeouts[recv_state]);
if ((except = vstream_setjmp(state->session->stream)) != 0)
RETURN(SENDING_MAIL ? smtp_stream_except(state, except,
xfer_states[recv_state]) : -1);
resp = smtp_chat_resp(state);
switch (recv_state) {
case SMTP_STATE_XFORWARD_NAME_ADDR:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_XFORWARD_NAME_ADDR]);
if (send_proto_helo)
recv_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_XFORWARD_PROTO_HELO:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_XFORWARD_PROTO_HELO]);
recv_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
smtp_mesg_fail(state, resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_MAIL]);
mail_from_rejected = 1;
}
recv_state = SMTP_STATE_RCPT;
break;
case SMTP_STATE_RCPT:
if (!mail_from_rejected) {
#ifdef notdef
if (resp->code == 552)
resp->code = 452;
#endif
rcpt = request->rcpt_list.info + recv_rcpt;
if (resp->code / 100 == 2) {
++nrcpt;
if (DEL_REQ_TRACE_ONLY(request->flags))
smtp_rcpt_done(state, resp->str, rcpt);
} else {
smtp_rcpt_fail(state, resp->code, rcpt,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_RCPT]);
}
}
if (++recv_rcpt == SMTP_RCPT_LEFT(state))
recv_state = DEL_REQ_TRACE_ONLY(request->flags) ?
SMTP_STATE_ABORT : SMTP_STATE_DATA;
break;
case SMTP_STATE_DATA:
if (resp->code / 100 != 3) {
if (nrcpt > 0)
smtp_mesg_fail(state, resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_DATA]);
nrcpt = -1;
}
recv_state = SMTP_STATE_DOT;
break;
case SMTP_STATE_DOT:
if (nrcpt > 0) {
if (resp->code / 100 != 2) {
smtp_mesg_fail(state, resp->code,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_DOT]);
} else {
for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (!SMTP_RCPT_ISMARKED(rcpt))
smtp_rcpt_done(state, resp->str, rcpt);
}
}
}
recv_state = (var_skip_quit_resp ?
SMTP_STATE_LAST : SMTP_STATE_QUIT);
break;
case SMTP_STATE_ABORT:
recv_state = (var_skip_quit_resp ?
SMTP_STATE_LAST : SMTP_STATE_QUIT);
break;
case SMTP_STATE_QUIT:
recv_state = SMTP_STATE_LAST;
break;
}
}
sndbuffree = sndbufsize;
if ((send_state == SMTP_STATE_RCPT && mail_from_rejected)
|| (send_state == SMTP_STATE_DATA && nrcpt == 0)
|| (send_state == SMTP_STATE_DOT && nrcpt < 0)) {
send_state = recv_state = SMTP_STATE_ABORT;
send_rcpt = recv_rcpt = 0;
vstring_strcpy(next_command, "RSET");
next_state = SMTP_STATE_QUIT;
next_rcpt = 0;
}
}
if (send_state == SMTP_STATE_LAST)
continue;
if (send_state == SMTP_STATE_DOT && nrcpt > 0) {
downgrading =
(var_disable_mime_oconv == 0
&& (state->features & SMTP_FEATURE_8BITMIME) == 0
&& strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) != 0);
if (downgrading)
state->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE
| MIME_OPT_REPORT_NESTING,
smtp_header_out,
(MIME_STATE_ANY_END) 0,
smtp_text_out,
(MIME_STATE_ANY_END) 0,
(MIME_STATE_ERR_PRINT) 0,
(void *) state);
state->space_left = var_smtp_line_limit;
smtp_timeout_setup(state->session->stream,
var_smtp_data1_tmout);
if ((except = vstream_setjmp(state->session->stream)) != 0)
RETURN(smtp_stream_except(state, except,
"sending message body"));
if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0)
msg_fatal("seek queue file: %m");
while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) {
if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
break;
if (downgrading == 0) {
smtp_text_out((void *) state, rec_type,
vstring_str(state->scratch),
VSTRING_LEN(state->scratch),
(off_t) 0);
} else {
mime_errs =
mime_state_update(state->mime_state, rec_type,
vstring_str(state->scratch),
VSTRING_LEN(state->scratch));
if (mime_errs) {
smtp_mesg_fail(state, 554,
"MIME 7-bit conversion failed: %s",
mime_state_error(mime_errs));
RETURN(0);
}
}
prev_type = rec_type;
}
if (state->mime_state) {
mime_errs =
mime_state_update(state->mime_state, rec_type, "", 0);
if (mime_errs) {
smtp_mesg_fail(state, 554,
"MIME 7-bit conversion failed: %s",
mime_state_error(mime_errs));
RETURN(0);
}
} else if (prev_type == REC_TYPE_CONT)
smtp_fputs("", 0, session->stream);
if ((state->features & SMTP_FEATURE_MAYBEPIX) != 0
&& request->arrival_time < vstream_ftime(session->stream)
- var_smtp_pix_thresh) {
msg_info("%s: enabling PIX <CRLF>.<CRLF> workaround for %s",
request->queue_id, session->namaddr);
vstream_fflush(session->stream);
sleep(var_smtp_pix_delay);
}
if (vstream_ferror(state->src))
msg_fatal("queue file read error");
if (rec_type != REC_TYPE_XTRA)
RETURN(mark_corrupt(state->src));
}
if (sndbuffree > 0)
sndbuffree -= VSTRING_LEN(next_command) + 2;
smtp_chat_cmd(state, "%s", vstring_str(next_command));
send_state = next_state;
send_rcpt = next_rcpt;
}
RETURN(0);
}