/* mutt - text oriented MIME mail user agent
* Copyright (C) 2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2005-2009 Brendan Cully <brendan@kublai.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/
/* This file contains code for direct SMTP delivery of email messages. */
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "mutt.h"
#include "mutt_curses.h"
#include "mutt_socket.h"
#ifdef USE_SSL
# include "mutt_ssl.h"
#endif
#ifdef USE_SASL_CYRUS
#include "mutt_sasl.h"
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#endif
#ifdef USE_SASL_GNU
#include "mutt_sasl_gnu.h"
#include <gsasl.h>
#endif
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/stat.h>
#define smtp_success(x) ((x)/100 == 2)
#define smtp_ready 334
#define smtp_continue 354
#define smtp_err_read -2
#define smtp_err_write -3
#define smtp_err_code -4
#define SMTP_PORT 25
#define SMTPS_PORT 465
#define SMTP_AUTH_SUCCESS 0
#define SMTP_AUTH_UNAVAIL 1
#define SMTP_AUTH_FAIL -1
enum {
STARTTLS,
AUTH,
DSN,
EIGHTBITMIME,
SMTPUTF8,
CAPMAX
};
static int smtp_auth (CONNECTION* conn);
static int smtp_auth_oauth (CONNECTION* conn, int xoauth2);
#ifdef USE_SASL_CYRUS
static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
#endif
#ifdef USE_SASL_GNU
static int smtp_auth_gsasl (CONNECTION* conn, const char* method);
#endif
static int smtp_fill_account (ACCOUNT* account);
static int smtp_open (CONNECTION* conn);
static int Esmtp = 0;
static char* AuthMechs = NULL;
static unsigned char Capabilities[(CAPMAX + 7)/ 8];
/* Note: the 'len' parameter is actually the number of bytes, as
* returned by mutt_socket_readln(). If all callers are converted to
* mutt_socket_buffer_readln() we can pass in the actual len, or
* perhaps the buffer itself.
*/
static int smtp_code (const char *buf, size_t len, int *n)
{
char code[4];
if (len < 4)
return -1;
code[0] = buf[0];
code[1] = buf[1];
code[2] = buf[2];
code[3] = 0;
if (mutt_atoi (code, n, 0) < 0)
return -1;
return 0;
}
/* Reads a command response from the SMTP server.
* Returns:
* 0 on success (2xx code) or continue (354 code)
* -1 write error, or any other response code
*/
static int
smtp_get_resp (CONNECTION * conn)
{
int n;
char buf[1024];
char *smtp_response;
do
{
n = mutt_socket_readln (buf, sizeof (buf), conn);
if (n < 4)
{
/* read error, or no response code */
return smtp_err_read;
}
smtp_response = buf + 3;
if (*smtp_response)
{
smtp_response++;
if (!ascii_strncasecmp ("8BITMIME", smtp_response, 8))
mutt_bit_set (Capabilities, EIGHTBITMIME);
else if (!ascii_strncasecmp ("AUTH ", smtp_response, 5))
{
mutt_bit_set (Capabilities, AUTH);
FREE (&AuthMechs);
AuthMechs = safe_strdup (smtp_response + 5);
}
else if (!ascii_strncasecmp ("DSN", smtp_response, 3))
mutt_bit_set (Capabilities, DSN);
else if (!ascii_strncasecmp ("STARTTLS", smtp_response, 8))
mutt_bit_set (Capabilities, STARTTLS);
else if (!ascii_strncasecmp ("SMTPUTF8", smtp_response, 8))
mutt_bit_set (Capabilities, SMTPUTF8);
}
if (smtp_code (buf, n, &n) < 0)
return smtp_err_code;
} while (buf[3] == '-');
if (smtp_success (n) || n == smtp_continue)
return 0;
mutt_error (_("SMTP session failed: %s"), buf);
return -1;
}
static int
smtp_get_auth_response (CONNECTION *conn, BUFFER *input_buf, int *smtp_rc,
BUFFER *response_buf)
{
const char *smtp_response;
mutt_buffer_clear (response_buf);
do
{
if (mutt_socket_buffer_readln (input_buf, conn) < 0)
return -1;
if (smtp_code (mutt_b2s (input_buf),
mutt_buffer_len (input_buf) + 1, /* number of bytes */
smtp_rc) < 0)
return -1;
if (*smtp_rc != smtp_ready)
break;
smtp_response = mutt_b2s (input_buf) + 3;
if (*smtp_response)
{
smtp_response++;
mutt_buffer_addstr (response_buf, smtp_response);
}
} while (mutt_b2s (input_buf)[3] == '-');
return 0;
}
static int
smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a)
{
char buf[1024];
int r;
while (a)
{
/* weed out group mailboxes, since those are for display only */
if (!a->mailbox || a->group)
{
a = a->next;
continue;
}
if (mutt_bit_isset (Capabilities, DSN) && DsnNotify)
snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
a->mailbox, DsnNotify);
else
snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox);
if (mutt_socket_write (conn, buf) == -1)
return smtp_err_write;
if ((r = smtp_get_resp (conn)))
return r;
a = a->next;
}
return 0;
}
static int
smtp_data (CONNECTION * conn, const char *msgfile)
{
char buf[1024];
FILE *fp = 0;
progress_t progress;
struct stat st;
int r, term = 0;
size_t buflen = 0;
fp = fopen (msgfile, "r");
if (!fp)
{
mutt_error (_("SMTP session failed: unable to open %s"), msgfile);
return -1;
}
stat (msgfile, &st);
unlink (msgfile);
mutt_progress_init (&progress, _("Sending message..."), MUTT_PROGRESS_SIZE,
NetInc, st.st_size);
snprintf (buf, sizeof (buf), "DATA\r\n");
if (mutt_socket_write (conn, buf) == -1)
{
safe_fclose (&fp);
return smtp_err_write;
}
if ((r = smtp_get_resp (conn)))
{
safe_fclose (&fp);
return r;
}
while (fgets (buf, sizeof (buf) - 1, fp))
{
buflen = mutt_strlen (buf);
term = buflen && buf[buflen-1] == '\n';
if (term && (buflen == 1 || buf[buflen - 2] != '\r'))
snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n");
if (buf[0] == '.')
{
if (mutt_socket_write_d (conn, ".", -1, MUTT_SOCK_LOG_FULL) == -1)
{
safe_fclose (&fp);
return smtp_err_write;
}
}
if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) == -1)
{
safe_fclose (&fp);
return smtp_err_write;
}
mutt_progress_update (&progress, ftell (fp), -1);
}
if (!term && buflen &&
mutt_socket_write_d (conn, "\r\n", -1, MUTT_SOCK_LOG_FULL) == -1)
{
safe_fclose (&fp);
return smtp_err_write;
}
safe_fclose (&fp);
/* terminate the message body */
if (mutt_socket_write (conn, ".\r\n") == -1)
return smtp_err_write;
if ((r = smtp_get_resp (conn)))
return r;
return 0;
}
/* Returns 1 if a contains at least one 8-bit character, 0 if none do.
*/
static int address_uses_unicode(const char *a)
{
if (!a)
return 0;
while (*a)
{
if ((unsigned char) *a & (1<<7))
return 1;
a++;
}
return 0;
}
/* Returns 1 if any address in a contains at least one 8-bit
* character, 0 if none do.
*/
static int addresses_use_unicode(const ADDRESS* a)
{
while (a)
{
if (a->mailbox && !a->group && address_uses_unicode(a->mailbox))
return 1;
a = a->next;
}
return 0;
}
int
mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
const ADDRESS* bcc, const char *msgfile, int eightbit)
{
CONNECTION *conn;
ACCOUNT account;
const char* envfrom;
char buf[1024];
int ret = -1;
/* it might be better to synthesize an envelope from from user and host
* but this condition is most likely arrived at accidentally */
if (EnvFrom)
envfrom = EnvFrom->mailbox;
else if (from)
envfrom = from->mailbox;
else
{
mutt_error (_("No from address given"));
return -1;
}
if (smtp_fill_account (&account) < 0)
return ret;
if (!(conn = mutt_conn_find (NULL, &account)))
return -1;
Esmtp = eightbit;
do
{
/* send our greeting */
if (( ret = smtp_open (conn)))
break;
FREE (&AuthMechs);
/* send the sender's address */
ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME))
{
safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
ret += 14;
}
if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
if (mutt_bit_isset (Capabilities, SMTPUTF8) &&
(address_uses_unicode(envfrom) ||
addresses_use_unicode(to) ||
addresses_use_unicode(cc) ||
addresses_use_unicode(bcc)))
ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8");
safe_strncat (buf, sizeof (buf), "\r\n", 3);
if (mutt_socket_write (conn, buf) == -1)
{
ret = smtp_err_write;
break;
}
if ((ret = smtp_get_resp (conn)))
break;
/* send the recipient list */
if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
|| (ret = smtp_rcpt_to (conn, bcc)))
break;
/* send the message data */
if ((ret = smtp_data (conn, msgfile)))
break;
mutt_socket_write (conn, "QUIT\r\n");
ret = 0;
}
while (0);
if (conn)
mutt_socket_close (conn);
if (ret == smtp_err_read)
mutt_error (_("SMTP session failed: read error"));
else if (ret == smtp_err_write)
mutt_error (_("SMTP session failed: write error"));
else if (ret == smtp_err_code)
mutt_error (_("Invalid server response"));
return ret;
}
static int smtp_fill_account (ACCOUNT* account)
{
static unsigned short SmtpPort = 0;
struct servent* service;
ciss_url_t url;
char* urlstr;
account->flags = 0;
account->port = 0;
account->type = MUTT_ACCT_TYPE_SMTP;
urlstr = safe_strdup (SmtpUrl);
url_parse_ciss (&url, urlstr);
if ((url.scheme != U_SMTP && url.scheme != U_SMTPS)
|| mutt_account_fromurl (account, &url) < 0)
{
FREE (&urlstr);
mutt_error (_("Invalid SMTP URL: %s"), SmtpUrl);
mutt_sleep (1);
return -1;
}
FREE (&urlstr);
if (url.scheme == U_SMTPS)
account->flags |= MUTT_ACCT_SSL;
if (!account->port)
{
if (account->flags & MUTT_ACCT_SSL)
account->port = SMTPS_PORT;
else
{
if (!SmtpPort)
{
service = getservbyname ("smtp", "tcp");
if (service)
SmtpPort = ntohs (service->s_port);
else
SmtpPort = SMTP_PORT;
dprint (3, (debugfile, "Using default SMTP port %d\n", SmtpPort));
}
account->port = SmtpPort;
}
}
return 0;
}
static int smtp_helo (CONNECTION* conn)
{
char buf[LONG_STRING];
const char* fqdn;
memset (Capabilities, 0, sizeof (Capabilities));
if (!Esmtp)
{
/* if TLS or AUTH are requested, use EHLO */
if (conn->account.flags & MUTT_ACCT_USER)
Esmtp = 1;
#ifdef USE_SSL
if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != MUTT_NO)
Esmtp = 1;
#endif
}
if (!(fqdn = mutt_fqdn (0)))
fqdn = NONULL (Hostname);
snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn);
/* XXX there should probably be a wrapper in mutt_socket.c that
* repeatedly calls conn->write until all data is sent. This
* currently doesn't check for a short write.
*/
if (mutt_socket_write (conn, buf) == -1)
return smtp_err_write;
return smtp_get_resp (conn);
}
static int smtp_open (CONNECTION* conn)
{
int rc;
if (mutt_socket_open (conn))
return -1;
/* get greeting string */
if ((rc = smtp_get_resp (conn)))
return rc;
if ((rc = smtp_helo (conn)))
return rc;
#ifdef USE_SSL
if (conn->ssf)
rc = MUTT_NO;
else if (option (OPTSSLFORCETLS))
rc = MUTT_YES;
else if (mutt_bit_isset (Capabilities, STARTTLS) &&
(rc = query_quadoption (OPT_SSLSTARTTLS,
_("Secure connection with TLS?"))) == -1)
return rc;
if (rc == MUTT_YES)
{
if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
return smtp_err_write;
if ((rc = smtp_get_resp (conn)))
return rc;
if (mutt_ssl_starttls (conn))
{
mutt_error (_("Could not negotiate TLS connection"));
mutt_sleep (1);
return -1;
}
/* re-EHLO to get authentication mechanisms */
if ((rc = smtp_helo (conn)))
return rc;
}
#endif
/* In some cases, the SMTP server will advertise AUTH even though
* it's not required. Check if the username is explicitly in the
* URL to decide whether to call smtp_auth().
*
* For client certificates, we assume the server won't advertise
* AUTH if they are pre-authenticated, because we also need to
* handle the post-TLS authentication (AUTH EXTERNAL) case.
*/
if (mutt_bit_isset (Capabilities, AUTH) &&
(conn->account.flags & MUTT_ACCT_USER
#ifdef USE_SSL
|| SslClientCert
#endif
))
{
return smtp_auth (conn);
}
return 0;
}
static int smtp_auth (CONNECTION* conn)
{
int r = SMTP_AUTH_UNAVAIL;
if (SmtpAuthenticators)
{
char* methods = safe_strdup (SmtpAuthenticators);
char* method;
char* delim;
for (method = methods; method; method = delim)
{
delim = strchr (method, ':');
if (delim)
*delim++ = '\0';
if (! method[0])
continue;
dprint (2, (debugfile, "smtp_authenticate: Trying method %s\n", method));
if (!ascii_strcasecmp (method, "oauthbearer"))
{
r = smtp_auth_oauth (conn, 0);
}
else if (!ascii_strcasecmp (method, "xoauth2"))
{
r = smtp_auth_oauth (conn, 1);
}
else
{
#if defined(USE_SASL_CYRUS)
r = smtp_auth_sasl (conn, method);
#elif defined(USE_SASL_GNU)
r = smtp_auth_gsasl (conn, method);
#else
mutt_error (_("SMTP authentication method %s requires SASL"), method);
mutt_sleep (1);
continue;
#endif
}
if (r == SMTP_AUTH_FAIL && delim)
{
mutt_error (_("%s authentication failed, trying next method"), method);
mutt_sleep (1);
}
else if (r != SMTP_AUTH_UNAVAIL)
break;
}
FREE (&methods);
}
else
{
#if defined(USE_SASL_CYRUS)
r = smtp_auth_sasl (conn, AuthMechs);
#elif defined(USE_SASL_GNU)
r = smtp_auth_gsasl (conn, NULL);
#else
mutt_error (_("SMTP authentication requires SASL"));
mutt_sleep (1);
r = SMTP_AUTH_UNAVAIL;
#endif
}
if (r != SMTP_AUTH_SUCCESS)
mutt_account_unsetpass (&conn->account);
if (r == SMTP_AUTH_FAIL)
{
mutt_error (_("SASL authentication failed"));
mutt_sleep (1);
}
else if (r == SMTP_AUTH_UNAVAIL)
{
mutt_error (_("No authenticators available"));
mutt_sleep (1);
}
return r == SMTP_AUTH_SUCCESS ? 0 : -1;
}
#ifdef USE_SASL_CYRUS
static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
{
sasl_conn_t* saslconn;
sasl_interact_t* interaction = NULL;
const char* mech;
const char* data = NULL;
unsigned int data_len;
BUFFER *temp_buf = NULL, *output_buf = NULL, *smtp_response_buf = NULL;
int rc = SMTP_AUTH_FAIL, sasl_rc, smtp_rc;
if (mutt_sasl_client_new (conn, &saslconn) < 0)
return SMTP_AUTH_FAIL;
do
{
sasl_rc = sasl_client_start (saslconn, mechlist, &interaction, &data,
&data_len, &mech);
if (sasl_rc == SASL_INTERACT)
mutt_sasl_interact (interaction);
}
while (sasl_rc == SASL_INTERACT);
if (sasl_rc != SASL_OK && sasl_rc != SASL_CONTINUE)
{
dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", NONULL (mechlist)));
sasl_dispose (&saslconn);
return SMTP_AUTH_UNAVAIL;
}
if (!option(OPTNOCURSES))
mutt_message (_("Authenticating (%s)..."), mech);
temp_buf = mutt_buffer_pool_get ();
output_buf = mutt_buffer_pool_get ();
smtp_response_buf = mutt_buffer_pool_get ();
mutt_buffer_printf (output_buf, "AUTH %s", mech);
if (data_len)
{
mutt_buffer_addch (output_buf, ' ');
mutt_buffer_to_base64 (temp_buf, (const unsigned char *)data, data_len);
mutt_buffer_addstr (output_buf, mutt_b2s (temp_buf));
}
mutt_buffer_addstr (output_buf, "\r\n");
do
{
if (mutt_socket_write (conn, mutt_b2s (output_buf)) < 0)
goto fail;
if (smtp_get_auth_response (conn, temp_buf, &smtp_rc, smtp_response_buf) < 0)
goto fail;
if (smtp_rc != smtp_ready)
break;
if (mutt_buffer_from_base64 (temp_buf, mutt_b2s (smtp_response_buf)) < 0)
{
dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n"));
goto fail;
}
do
{
sasl_rc = sasl_client_step (saslconn, mutt_b2s (temp_buf),
mutt_buffer_len (temp_buf),
&interaction, &data, &data_len);
if (sasl_rc == SASL_INTERACT)
mutt_sasl_interact (interaction);
}
while (sasl_rc == SASL_INTERACT);
if (data_len)
mutt_buffer_to_base64 (output_buf, (const unsigned char *)data, data_len);
else
mutt_buffer_clear (output_buf);
mutt_buffer_addstr (output_buf, "\r\n");
}
while (sasl_rc != SASL_FAIL);
if (smtp_success (smtp_rc))
{
mutt_sasl_setup_conn (conn, saslconn);
rc = SMTP_AUTH_SUCCESS;
}
else
{
if (smtp_rc == smtp_ready)
mutt_socket_write (conn, "*\r\n");
sasl_dispose (&saslconn);
}
fail:
mutt_buffer_pool_release (&temp_buf);
mutt_buffer_pool_release (&output_buf);
mutt_buffer_pool_release (&smtp_response_buf);
return rc;
}
#endif /* USE_SASL_CYRUS */
#ifdef USE_SASL_GNU
static int smtp_auth_gsasl (CONNECTION *conn, const char *method)
{
Gsasl_session *gsasl_session = NULL;
const char *chosen_mech;
BUFFER *input_buf = NULL, *output_buf = NULL, *smtp_response_buf = NULL;
char *gsasl_step_output = NULL;
int rc = SMTP_AUTH_FAIL, gsasl_rc = GSASL_OK, smtp_rc;
chosen_mech = mutt_gsasl_get_mech (method, AuthMechs);
if (!chosen_mech)
{
dprint (2, (debugfile, "mutt_gsasl_get_mech() returned no usable mech\n"));
return SMTP_AUTH_UNAVAIL;
}
dprint (2, (debugfile, "smtp_auth_gsasl: using mech %s\n", chosen_mech));
if (mutt_gsasl_client_new (conn, chosen_mech, &gsasl_session) < 0)
{
dprint (1, (debugfile,
"smtp_auth_gsasl: Error allocating GSASL connection.\n"));
return SMTP_AUTH_UNAVAIL;
}
if (!option(OPTNOCURSES))
mutt_message (_("Authenticating (%s)..."), chosen_mech);
input_buf = mutt_buffer_pool_get ();
output_buf = mutt_buffer_pool_get ();
smtp_response_buf = mutt_buffer_pool_get ();
mutt_buffer_printf (output_buf, "AUTH %s", chosen_mech);
/* Work around broken SMTP servers. See Debian #1010658.
* The msmtp source also forces IR for PLAIN because the author
* encountered difficulties with a server requiring it.
*/
if (!mutt_strcmp (chosen_mech, "PLAIN"))
{
gsasl_rc = gsasl_step64 (gsasl_session, "", &gsasl_step_output);
if (gsasl_rc != GSASL_NEEDS_MORE && gsasl_rc != GSASL_OK)
{
dprint (1, (debugfile, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
gsasl_strerror (gsasl_rc)));
goto fail;
}
mutt_buffer_addch (output_buf, ' ');
mutt_buffer_addstr (output_buf, gsasl_step_output);
gsasl_free (gsasl_step_output);
}
mutt_buffer_addstr (output_buf, "\r\n");
do
{
if (mutt_socket_write (conn, mutt_b2s (output_buf)) < 0)
goto fail;
if (smtp_get_auth_response (conn, input_buf, &smtp_rc, smtp_response_buf) < 0)
goto fail;
if (smtp_rc != smtp_ready)
break;
gsasl_rc = gsasl_step64 (gsasl_session, mutt_b2s (smtp_response_buf),
&gsasl_step_output);
if (gsasl_rc == GSASL_NEEDS_MORE || gsasl_rc == GSASL_OK)
{
mutt_buffer_strcpy (output_buf, gsasl_step_output);
mutt_buffer_addstr (output_buf, "\r\n");
gsasl_free (gsasl_step_output);
}
else
{
dprint (1, (debugfile, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
gsasl_strerror (gsasl_rc)));
}
}
while (gsasl_rc == GSASL_NEEDS_MORE || gsasl_rc == GSASL_OK);
if (smtp_rc == smtp_ready)
{
mutt_socket_write (conn, "*\r\n");
goto fail;
}
if (smtp_success (smtp_rc) && (gsasl_rc == GSASL_OK))
rc = SMTP_AUTH_SUCCESS;
fail:
mutt_buffer_pool_release (&input_buf);
mutt_buffer_pool_release (&output_buf);
mutt_buffer_pool_release (&smtp_response_buf);
mutt_gsasl_client_finish (&gsasl_session);
if (rc == SMTP_AUTH_FAIL)
dprint (2, (debugfile, "smtp_auth_gsasl: %s failed\n", chosen_mech));
return rc;
}
#endif
/* smtp_auth_oauth: AUTH=OAUTHBEARER support. See RFC 7628 */
static int smtp_auth_oauth (CONNECTION* conn, int xoauth2)
{
int rc = SMTP_AUTH_FAIL, smtp_rc;
BUFFER *bearertoken = NULL, *authline = NULL;
BUFFER *input_buf = NULL, *smtp_response_buf = NULL;
const char *authtype;
authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
/* L10N:
%s is the authentication type, such as XOAUTH2 or OAUTHBEARER
*/
mutt_message (_("Authenticating (%s)..."), authtype);;
bearertoken = mutt_buffer_pool_get ();
authline = mutt_buffer_pool_get ();
input_buf = mutt_buffer_pool_get ();
smtp_response_buf = mutt_buffer_pool_get ();
/* We get the access token from the smtp_oauth_refresh_command */
if (mutt_account_getoauthbearer (&conn->account, bearertoken, xoauth2))
goto cleanup;
if (xoauth2)
{
mutt_buffer_printf (authline, "AUTH %s\r\n", authtype);
if (mutt_socket_write (conn, mutt_b2s (authline)) == -1)
goto cleanup;
if (smtp_get_auth_response (conn, input_buf, &smtp_rc, smtp_response_buf) < 0)
goto saslcleanup;
if (smtp_rc != smtp_ready)
goto saslcleanup;
mutt_buffer_printf (authline, "%s\r\n", mutt_b2s (bearertoken));
}
else
{
mutt_buffer_printf (authline, "AUTH %s %s\r\n", authtype, mutt_b2s (bearertoken));
}
if (mutt_socket_write (conn, mutt_b2s (authline)) == -1)
goto cleanup;
if (smtp_get_auth_response (conn, input_buf, &smtp_rc, smtp_response_buf) < 0)
goto saslcleanup;
if (!smtp_success (smtp_rc))
goto saslcleanup;
rc = SMTP_AUTH_SUCCESS;
saslcleanup:
if (rc != SMTP_AUTH_SUCCESS)
{
/* The error response was in SASL continuation, so continue the SASL
* to cause a failure and exit SASL input. See RFC 7628 3.2.3
* "AQ==" is Base64 encoded ^A (0x01) .
*/
mutt_socket_write (conn, "AQ==\r\n");
smtp_get_resp (conn);
}
cleanup:
mutt_buffer_pool_release (&bearertoken);
mutt_buffer_pool_release (&authline);
mutt_buffer_pool_release (&input_buf);
mutt_buffer_pool_release (&smtp_response_buf);
return rc;
}