-/*
- * smtp.c -- nmh SMTP interface
+/* smtp.c -- nmh SMTP interface
*
* This code is Copyright (c) 2002, by the authors of nmh. See the
* COPYRIGHT file in the root directory of the nmh distribution for
#include <h/netsec.h>
#include <sys/socket.h>
+#include "sbr/base64.h"
/*
* This module implements an interface to SendMail very similar
* RP_-style codes.
*/
-/*
- * On older 4.2BSD machines without the POSIX function `sigaction',
- * the alarm handing stuff for time-outs will NOT work due to the way
- * syscalls get restarted. This is not really crucial, since SendMail
- * is generally well-behaved in this area.
- */
-
-#ifdef SENDMAILBUG
-/*
- * It appears that some versions of Sendmail will return Code 451
- * when they don't really want to indicate a failure.
- * "Code 451 almost always means sendmail has deferred; we don't
- * really want bomb out at this point since sendmail will rectify
- * things later." So, if you define SENDMAILBUG, Code 451 is
- * considered the same as Code 250. Yuck!
- */
-#endif
-
#define NBITS ((sizeof (int)) * 8)
-/*
- * these codes must all be different!
- */
+/* Timeout in seconds for SMTP commands.
+ * Lore has it they must be distinct. */
#define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
#define SM_HELO 20
#define SM_RSET 15
#define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
#define SM_RCPT 302 /* see above */
#define SM_DATA 120 /* see above */
-#define SM_TEXT 180 /* see above */
#define SM_DOT 600 /* see above */
#define SM_QUIT 30
-#define SM_CLOS 10
-#define SM_AUTH 45
static int sm_addrs = 0;
static int sm_child = NOTOK;
static int sm_debug = 0;
-static int sm_nl = TRUE;
+static bool sm_nl = true;
static int sm_verbose = 0;
static netsec_context *nsc = NULL;
static int sendmail_init (char *, int, int, int, int, const char *,
const char *);
-static int rclient (char *, char *);
-static int sm_ierror (const char *fmt, ...);
+static int rclient (char *, char *, char **);
+static int sm_ierror (const char *fmt, ...) CHECK_PRINTF(1, 2);
static int sm_nerror (char *);
-static int smtalk (int time, char *fmt, ...);
+static int smtalk (int time, char *fmt, ...) CHECK_PRINTF(2, 3);
static int sm_wstream (char *, int);
static int smhear (void);
-static char *EHLOset (char *);
+static char *EHLOset (char *) PURE;
static int sm_sasl_callback(enum sasl_message_type, unsigned const char *,
unsigned int, unsigned char **, unsigned int *,
char **);
const char *oauth_svc, int tls)
{
int result, sd1;
- char *errstr;
+ char *errstr, *chosen_server;
if (watch)
- verbose = TRUE;
+ verbose = true;
sm_verbose = verbose;
sm_debug = debug;
} else {
client = LocalName(1); /* no clientname -> LocalName */
}
- }
- /*
- * Last-ditch check just in case client still isn't set to anything
- */
-
- if (client == NULL || *client == '\0')
- client = "localhost";
+ /*
+ * Last-ditch check just in case client still isn't set to anything
+ */
+ if (client == NULL || *client == '\0')
+ client = "localhost";
+ }
nsc = netsec_init();
if (user)
netsec_set_userid(nsc, user);
+ if ((sd1 = rclient (server, port, &chosen_server)) == NOTOK)
+ return RP_BHST;
+
+ SIGNAL (SIGPIPE, SIG_IGN);
+
+ netsec_set_fd(nsc, sd1, sd1);
+
+ netsec_set_hostname(nsc, chosen_server);
+
if (sm_debug)
netsec_set_snoop(nsc, 1);
if (sasl) {
- if (netsec_set_sasl_params(nsc, server, "smtp", saslmech,
- sm_sasl_callback, &errstr) != OK)
+ if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback,
+ &errstr) != OK)
return sm_nerror(errstr);
}
return sm_ierror("OAuth2 not supported");
}
- if ((sd1 = rclient (server, port)) == NOTOK)
- return RP_BHST;
-
- SIGNAL (SIGPIPE, SIG_IGN);
-
- netsec_set_fd(nsc, sd1, sd1);
-
- if (tls) {
- if (netsec_set_tls(nsc, 1, &errstr) != OK)
+ if (tls & S_TLSENABLEMASK) {
+ if (netsec_set_tls(nsc, 1, tls & S_NOVERIFY, &errstr) != OK)
return sm_nerror(errstr);
}
/*
- * If tls == 2, that means that the user requested "initial" TLS,
- * which happens right after the connection has opened. Do that
- * negotiation now
+ * If tls == S_INITTLS, that means that the user requested
+ * "initial" TLS, which happens right after the connection has
+ * opened. Do that negotiation now
*/
- if (tls == 2) {
+ if (tls & S_INITTLS) {
if (netsec_negotiate_tls(nsc, &errstr) != OK) {
sm_end(NOTOK);
return sm_nerror(errstr);
* restart the EHLO dialog after TLS negotiation is complete.
*/
- if (tls == 1) {
+ if (tls & S_STARTTLS) {
if (! EHLOset("STARTTLS")) {
sm_end(NOTOK);
return sm_ierror("SMTP server does not support TLS");
char *vec[15], *errstr;
if (watch)
- verbose = TRUE;
+ verbose = true;
sm_verbose = verbose;
sm_debug = debug;
client = clientname;
else
client = LocalName(1); /* no clientname -> LocalName */
- }
-
- /*
- * Last-ditch check just in case client still isn't set to anything
- */
- if (client == NULL || *client == '\0')
- client = "localhost";
+ /*
+ * Last-ditch check just in case client still isn't set to anything
+ */
+ if (client == NULL || *client == '\0')
+ client = "localhost";
+ }
nsc = netsec_init();
if (user)
netsec_set_userid(nsc, user);
+ netsec_set_hostname(nsc, client);
+
if (sm_debug)
netsec_set_snoop(nsc, 1);
if (sasl) {
- if (netsec_set_sasl_params(nsc, client, "smtp", saslmech,
- sm_sasl_callback, &errstr) != OK)
+ if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback,
+ &errstr) != OK)
return sm_nerror(errstr);
}
return sm_ierror ("no pipes");
}
- for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
- sleep (5);
-
+ sm_child = fork();
switch (sm_child) {
case NOTOK:
close (pdo[0]);
close (pdi[1]);
close (pdo[0]);
- netsec_set_fd(nsc, pdi[i], pdo[1]);
+ netsec_set_fd(nsc, pdi[0], pdo[1]);
netsec_set_timeout(nsc, SM_OPEN);
result = smhear ();
switch (result) {
}
static int
-rclient (char *server, char *service)
+rclient (char *server, char *service, char **chosen_server)
{
int sd;
char response[BUFSIZ];
+ if (server == NULL)
+ server = servers;
+
+ *chosen_server = server;
+
if ((sd = client (server, service, response, sizeof(response),
sm_debug)) != NOTOK)
return sd;
if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
mail_parameters = " BODY=8BITMIME SMTPUTF8";
} else {
- advise (NULL, "SMTP server does not support %s, not sending.\n"
+ inform("SMTP server does not support %s, not sending.\n"
"Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
sm_end (NOTOK);
if (EHLOset ("8BITMIME")) {
mail_parameters = " BODY=8BITMIME";
} else {
- advise (NULL, "SMTP server does not support 8BITMIME, not sending.\n"
+ inform("SMTP server does not support 8BITMIME, not sending.\n"
"Suggest encoding message for 7-bit transport by setting your\n"
"locale to C, and/or specifying *b64 in mhbuild directives.");
sm_end (NOTOK);
{
switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
: "RCPT TO:<%s%s>",
- path ? path : "", mbox, host)) {
+ FENDNULL(path), mbox, host)) {
case 250:
case 251:
sm_addrs++;
return RP_OK;
case 451:
-#ifdef SENDMAILBUG
- sm_addrs++;
- return RP_OK;
-#endif /* SENDMAILBUG */
case 421:
case 450:
case 452:
{
switch (smtalk (SM_DATA, "DATA")) {
case 354:
- sm_nl = TRUE;
+ sm_nl = true;
return RP_OK;
case 451:
-#ifdef SENDMAILBUG
- sm_nl = TRUE;
- return RP_OK;
-#endif /* SENDMAILBUG */
case 421:
return RP_NO;
return RP_OK;
case 451:
-#ifdef SENDMAILBUG
- return RP_OK;
-#endif /* SENDMAILBUG */
case 452:
default:
return RP_NO;
case NOTOK:
sm_note.code = sm_reply.code;
sm_note.length = sm_reply.length;
- memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
+ memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);
+ /* FALLTHRU */
case DONE:
if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
return RP_OK;
}
if (nsc != NULL) {
- netsec_shutdown(nsc, 1);
+ netsec_shutdown(nsc);
nsc = NULL;
}
for (bp = buffer; bp && len > 0; bp++, len--) {
switch (*bp) {
case '\n':
- sm_nl = TRUE;
+ sm_nl = true;
if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
sm_nerror(errstr);
return NOTOK;
if (netsec_write(nsc, ".", 1, &errstr) != OK) {
sm_nerror(errstr);
return NOTOK;
- } /* FALL THROUGH */
+ }
+ /* FALLTHRU */
default:
- sm_nl = FALSE;
+ sm_nl = false;
}
if (netsec_write(nsc, bp, 1, &errstr) != OK) {
sm_nerror(errstr);
static int
smhear (void)
{
- int i, code, cont, more;
+ int i, code;
+ bool cont, more;
size_t buflen, rc;
unsigned char *bp;
char *rp;
rp = sm_reply.text;
rc = sizeof(sm_reply.text) - 1;
- for (more = FALSE; (buffer = netsec_readline(nsc, &buflen,
+ for (more = false; (buffer = netsec_readline(nsc, &buflen,
&errstr)) != NULL ; ) {
if (doingEHLO
- && strncmp (buffer, "250", LEN("250")) == 0
+ && has_prefix(buffer, "250")
&& (buffer[3] == '-' || doingEHLO == 2)
&& buffer[4]) {
if (doingEHLO == 2) {
for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
continue;
- cont = FALSE;
+ cont = false;
code = atoi ((char *) bp);
bp += 3, buflen -= 3;
for (; buflen > 0 && isspace (*bp); bp++, buflen--)
continue;
if (buflen > 0 && *bp == '-') {
- cont = TRUE;
+ cont = true;
bp++, buflen--;
for (; buflen > 0 && isspace (*bp); bp++, buflen--)
continue;
if (more) {
if (code != sm_reply.code || cont)
continue;
- more = FALSE;
+ more = false;
} else {
sm_reply.code = code;
more = cont;
continue;
if (sm_reply.code < 100) {
if (sm_verbose) {
- printf ("%s\n", sm_reply.text);
+ puts(sm_reply.text);
fflush (stdout);
}
goto again;
rp_string (int code)
{
char *text;
- static char buffer[BUFSIZ];
+ /* The additional space is to avoid warning from gcc -Wformat-truncation. */
+ static char buffer[BUFSIZ + 19];
switch (sm_reply.code != NOTOK ? code : NOTOK) {
case RP_AOK:
for (ehlo = EHLOkeys; *ehlo; ehlo++) {
ep = *ehlo;
- if (strncmp (ep, s, len) == 0) {
+ if (has_prefix(ep, s)) {
for (ep += len; *ep == ' '; ep++)
continue;
return ep;
return NOTOK;
}
- if (strncmp(line, "334 ", 4) != 0) {
+ if (!has_prefix(line, "334 ")) {
netsec_err(errstr, "Improper SASL protocol response: %s", line);
return NOTOK;
}
if (line == NULL)
return NOTOK;
- if (strncmp(line, "235 ", 4) != 0) {
+ if (!has_prefix(line, "235 ")) {
if (len > 4)
netsec_err(errstr, "Authentication failed: %s", line + 4);
else