On Tuesday, 06 September 2005 at 22:49, Brendan Cully wrote: > On Tuesday, 06 September 2005 at 21:09, Brendan Cully wrote: > > On Tuesday, 06 September 2005 at 17:14, Brendan Cully wrote: > > > On Tuesday, 06 September 2005 at 16:50, Brendan Cully wrote: > > > > On Tuesday, 06 September 2005 at 11:39, Brendan Cully wrote: > > > > > Attached is Michael Elkins' SMTP relay patch patch.me.smtp.3 ported to > > > > > 1.5.10. I've replaced smtp_host with smtp_url so you can override the > > > > > port, and because I intend to add AUTH and TLS support to it > > > > > shortly. It's such a tiny patch that I have a hard time considering it > > > > > to be bloat, and if it isn't very full-featured, nothing stops you > > > > > from using the standard process-based interface. > > > > > > > > Here's the latest version, supporting TLS and SMTP authentication (if > > > > you enable SASL). The SASL support code is probably longer than the > > > > actual mail delivery bits :). As with IMAP and POP, you can use the > > > > $smtp_authenticators variable to control which methods mutt will use > > > > to authenticate to the SMTP server. > > > > > > > > Test away. I think I'd like to see this go in. > > > > > > Whoops, I forgot a bit necessary for account-hooks to work on SMTP > > > accounts. Here's the latest. > > > > Last one tonight :) > > > > This one adds support for 8BITMIME. TLS and AUTH already required > > minimal ESMTP handling so it was trivial to add. > > Ok, one more. DSN support was also simple to add. This one is against bleeding-edge CVS (it needs the new mutt_progress_bar function) and it, um, displays a progress bar while sending. And now I really will go to sleep.
diff -r e11087daff04 Makefile.am --- a/Makefile.am Wed Sep 7 14:45:49 2005 +++ b/Makefile.am Wed Sep 7 01:12:06 2005 @@ -61,7 +61,7 @@ EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \ mutt_tunnel.c pop.c pop_auth.c pop_lib.c smime.c pgp.c pgpinvoke.c pgpkey.c \ pgplib.c sha1.c pgpmicalg.c gnupgparse.c resize.c dotlock.c remailer.c \ - browser.h mbyte.h remailer.h url.h \ + smtp.c browser.h mbyte.h remailer.h url.h \ crypt-mod-pgp-classic.c crypt-mod-smime-classic.c \ pgppacket.c mutt_idna.h hcache.c mutt_ssl_gnutls.c \ crypt-gpgme.c crypt-mod-pgp-gpgme.c crypt-mod-smime-gpgme.c diff -r e11087daff04 account.c --- a/account.c Wed Sep 7 14:45:49 2005 +++ b/account.c Wed Sep 7 01:12:06 2005 @@ -123,6 +123,11 @@ } #endif +#ifdef USE_SMTP + if (account->type == M_ACCT_TYPE_SMTP) + url->scheme = U_SMTP; +#endif + url->host = account->host; if (account->flags & M_ACCT_PORT) url->port = account->port; diff -r e11087daff04 account.h --- a/account.h Wed Sep 7 14:45:49 2005 +++ b/account.h Wed Sep 7 01:12:06 2005 @@ -28,7 +28,8 @@ { M_ACCT_TYPE_NONE = 0, M_ACCT_TYPE_IMAP, - M_ACCT_TYPE_POP + M_ACCT_TYPE_POP, + M_ACCT_TYPE_SMTP }; /* account flags */ diff -r e11087daff04 configure.in --- a/configure.in Wed Sep 7 14:45:49 2005 +++ b/configure.in Wed Sep 7 01:12:06 2005 @@ -499,6 +499,13 @@ fi ]) AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes) + +AC_ARG_ENABLE(smtp, AC_HELP_STRING([--enable-smtp], [include internal SMTP relay support]), + [if test $enableval = yes; then + AC_DEFINE(USE_SMTP, 1, [Include internal SMTP relay support]) + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS smtp.o" + need_socket="yes" + fi]) dnl -- end socket dependencies -- diff -r e11087daff04 globals.h --- a/globals.h Wed Sep 7 14:45:49 2005 +++ b/globals.h Wed Sep 7 01:12:06 2005 @@ -109,6 +109,10 @@ WHERE char *Shell; WHERE char *Signature; WHERE char *SimpleSearch; +#if USE_SMTP +WHERE char *SmtpUrl INITVAL (NULL); +WHERE char *SmtpAuthenticators INITVAL (NULL); +#endif /* USE_SMTP */ WHERE char *Spoolfile; WHERE char *SpamSep; #if defined(USE_SSL) || defined(USE_GNUTLS) diff -r e11087daff04 init.h --- a/init.h Wed Sep 7 14:45:49 2005 +++ b/init.h Wed Sep 7 01:12:06 2005 @@ -91,6 +91,9 @@ # endif # ifndef USE_POP # define USE_POP +# endif +# ifndef USE_SMTP +# define USE_SMTP # endif # ifndef USE_SSL # define USE_SSL @@ -2497,6 +2500,34 @@ ** messages from the current folder. The default is to pause one second, so ** a value of zero for this option suppresses the pause. */ +#if USE_SMTP + { "smtp_url", DT_STR, R_NONE, UL &SmtpUrl, NULL }, + /* + ** .pp + ** Defines the SMTP ``smart'' host where sent messages should relayed for + ** delivery. This should take the form of an SMTP URL, eg: + ** .pp + ** smtp://[user[:pass]@]host[:port]/ + ** .pp + ** Setting this variable overrides the value of the ``$$sendmail'' + ** variable. + */ +# ifdef USE_SASL + { "smtp_authenticators", DT_STR, R_NONE, UL &SmtpAuthenticators, UL 0 }, + /* + ** .pp + ** This is a colon-delimited list of authentication methods mutt may + ** attempt to use to log in to an SMTP server, in the order mutt should + ** try them. Authentication methods are any SASL mechanism, eg + ** 'digest-md5', 'gssapi' or 'cram-md5'. + ** This parameter is case-insensitive. If this parameter is unset + ** (the default) mutt will try all available methods, in order from + ** most-secure to least-secure. + ** .pp + ** Example: set imap_authenticators="digest-md5:cram-md5" + */ +# endif /* USE_SASL */ +#endif /* USE_SMTP */ { "sort", DT_SORT, R_INDEX|R_RESORT, UL &Sort, SORT_DATE }, /* ** .pp diff -r e11087daff04 mutt_sasl.c --- a/mutt_sasl.c Wed Sep 7 14:45:49 2005 +++ b/mutt_sasl.c Wed Sep 7 01:12:06 2005 @@ -182,42 +182,44 @@ case M_ACCT_TYPE_POP: service = "pop"; break; + case M_ACCT_TYPE_SMTP: + service = "smtp"; + break; default: - dprint (1, (debugfile, "mutt_sasl_client_new: account type unset\n")); + mutt_error (_("Unknown SASL profile")); return -1; } size = sizeof (local); - if (getsockname (conn->fd, (struct sockaddr *)&local, &size)){ - dprint (1, (debugfile, "mutt_sasl_client_new: getsockname for local failed\n")); + if (getsockname (conn->fd, (struct sockaddr *)&local, &size)) { + mutt_error (_("SASL failed to get local IP address")); return -1; } else - if (iptostring((struct sockaddr *)&local, size, iplocalport, IP_PORT_BUFLEN) != SASL_OK){ - dprint (1, (debugfile, "mutt_sasl_client_new: iptostring for local failed\n")); + if (iptostring((struct sockaddr *)&local, size, iplocalport, IP_PORT_BUFLEN) != SASL_OK) { + mutt_error (_("SASL failed to parse local IP address")); return -1; } size = sizeof (remote); if (getpeername (conn->fd, (struct sockaddr *)&remote, &size)){ - dprint (1, (debugfile, "mutt_sasl_client_new: getsockname for remote failed\n")); + mutt_error (_("SASL failed to get remote IP address")); return -1; } else if (iptostring((struct sockaddr *)&remote, size, ipremoteport, IP_PORT_BUFLEN) != SASL_OK){ - dprint (1, (debugfile, "mutt_sasl_client_new: iptostring for remote failed\n")); - return -1; - } - - dprint(1,(debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport)); + mutt_error (_("SASL failed to parse remote IP address")); + return -1; + } + + dprint(2, (debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport)); rc = sasl_client_new (service, conn->account.host, iplocalport, ipremoteport, mutt_sasl_get_callbacks (&conn->account), 0, saslconn); if (rc != SASL_OK) { - dprint (1, (debugfile, - "mutt_sasl_client_new: Error allocating SASL connection\n")); + mutt_error (_("Error allocating SASL connection")); return -1; } @@ -232,8 +234,7 @@ secprops.security_flags |= SASL_SEC_NOPLAINTEXT; if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) { - dprint (1, (debugfile, - "mutt_sasl_client_new: Error setting security properties\n")); + mutt_error (_("Error setting SASL security properties")); return -1; } @@ -243,13 +244,13 @@ dprint (2, (debugfile, "External SSF: %d\n", conn->ssf)); if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK) { - dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n")); + mutt_error (_("Error setting SASL external security strength")); return -1; } dprint (2, (debugfile, "External authentication name: %s\n", conn->account.user)); if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK) - { - dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external properties\n")); + { + mutt_error (_("Error setting SASL external user name")); return -1; } } diff -r e11087daff04 mutt_socket.h --- a/mutt_socket.h Wed Sep 7 14:45:49 2005 +++ b/mutt_socket.h Wed Sep 7 01:12:06 2005 @@ -56,7 +56,7 @@ int mutt_socket_readchar (CONNECTION *conn, char *c); #define mutt_socket_readln(A,B,C) mutt_socket_readln_d(A,B,C,M_SOCK_LOG_CMD) int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION *conn, int dbg); -#define mutt_socket_write(A,B) mutt_socket_write_d(A,B,M_SOCK_LOG_CMD); +#define mutt_socket_write(A,B) mutt_socket_write_d(A,B,M_SOCK_LOG_CMD) int mutt_socket_write_d (CONNECTION *conn, const char *buf, int dbg); /* stupid hack for imap_logout_all */ diff -r e11087daff04 protos.h --- a/protos.h Wed Sep 7 14:45:49 2005 +++ b/protos.h Wed Sep 7 01:12:06 2005 @@ -342,6 +342,10 @@ int _mutt_save_message (HEADER *, CONTEXT *, int, int, int); int mutt_save_message (HEADER *, int, int, int, int *); int mutt_search_command (int, int); +#ifdef USE_SMTP +int mutt_smtp_send (const ADDRESS *, const ADDRESS *, const ADDRESS *, + const ADDRESS *, const char *, int); +#endif int mutt_strwidth (const char *); int mutt_compose_menu (HEADER *, char *, size_t, HEADER *); int mutt_thread_set_flag (HEADER *, int, int, int); diff -r e11087daff04 send.c --- a/send.c Wed Sep 7 14:45:49 2005 +++ b/send.c Wed Sep 7 01:12:06 2005 @@ -990,8 +990,16 @@ return mix_send_message (msg->chain, tempfile); #endif +#if USE_SMTP + if (SmtpUrl) + return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc, + msg->env->bcc, tempfile, + (msg->content->encoding == ENC8BIT)); +#endif /* USE_SMTP */ + i = mutt_invoke_sendmail (msg->env->from, msg->env->to, msg->env->cc, - msg->env->bcc, tempfile, (msg->content->encoding == ENC8BIT)); + msg->env->bcc, tempfile, + (msg->content->encoding == ENC8BIT)); return (i); } diff -r e11087daff04 sendlib.c --- a/sendlib.c Wed Sep 7 14:45:49 2005 +++ b/sendlib.c Wed Sep 7 01:12:06 2005 @@ -2169,6 +2169,12 @@ mutt_copy_bytes (fp, f, h->content->length); fclose (f); +#if USE_SMTP + if (SmtpUrl) + ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile, + h->content->encoding == ENC8BIT); + else +#endif /* USE_SMTP */ ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile, h->content->encoding == ENC8BIT); } diff -r e11087daff04 url.c --- a/url.c Wed Sep 7 14:45:49 2005 +++ b/url.c Wed Sep 7 01:12:06 2005 @@ -37,10 +37,11 @@ { "file", U_FILE }, { "imap", U_IMAP }, { "imaps", U_IMAPS }, - { "pop", U_POP }, - { "pops", U_POPS }, + { "pop", U_POP }, + { "pops", U_POPS }, { "mailto", U_MAILTO }, - { NULL, U_UNKNOWN} + { "smtp", U_SMTP }, + { NULL, U_UNKNOWN } }; diff -r e11087daff04 url.h --- a/url.h Wed Sep 7 14:45:49 2005 +++ b/url.h Wed Sep 7 01:12:06 2005 @@ -8,6 +8,7 @@ U_POPS, U_IMAP, U_IMAPS, + U_SMTP, U_MAILTO, U_UNKNOWN } diff -r e11087daff04 smtp.c --- /dev/null Wed Sep 7 14:45:49 2005 +++ b/smtp.c Wed Sep 7 01:12:06 2005 @@ -0,0 +1,527 @@ +/* mutt - text oriented MIME mail user agent + * Copyright (C) 2002 Michael R. Elkins <me@xxxxxxxx> + * Copyright (C) 2005 Brendan Cully <brendan@xxxxxxxxxx> + * + * 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" +#if defined(USE_SSL) || defined(USE_GNUTLS) +# include "mutt_ssl.h" +#endif +#ifdef USE_SASL +#include "mutt_sasl.h" + +#include <sasl/sasl.h> +#include <sasl/saslutil.h> +#endif + +#include <netdb.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_PORT 25 + +#define SMTP_AUTH_SUCCESS 0 +#define SMTP_AUTH_UNAVAIL 1 +#define SMTP_AUTH_FAIL -1 + +enum { + STARTTLS, + AUTH, + DSN, + EIGHTBITMIME, + + CAPMAX +}; + +#ifdef USE_SASL +static int smtp_auth (CONNECTION* conn); +static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms); +#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]; + +/* 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]; + + do { + n = mutt_socket_readln (buf, sizeof (buf), conn); + if (n == -1) + return smtp_err_read; + n = atoi (buf); + + if (!ascii_strncasecmp ("8BITMIME", buf + 4, 8)) + mutt_bit_set (Capabilities, EIGHTBITMIME); + else if (!ascii_strncasecmp ("AUTH", buf + 4, 4)) + { + mutt_bit_set (Capabilities, AUTH); + AuthMechs = safe_strdup (buf + 9); + } + else if (!ascii_strncasecmp ("DSN", buf + 4, 3)) + mutt_bit_set (Capabilities, DSN); + else if (!ascii_strncasecmp ("STARTTLS", buf + 4, 8)) + mutt_bit_set (Capabilities, STARTTLS); + } while (buf[3] == '-'); + + if (smtp_success (n) || n == smtp_continue) + return 0; + + mutt_error (_("SMTP session failed: %s"), buf); + return -1; +} + +static int +smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a) +{ + char buf[1024]; + int r; + + while (a) + { + 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; + + fp = fopen (msgfile, "r"); + if (!fp) + { + mutt_error ("SMTP session failed: unable to open %s", msgfile); + return -1; + } + stat (msgfile, &st); + progress.msg = _("Sending message..."); + progress.size = st.st_size; + mutt_progress_bar (&progress, 0); + + snprintf (buf, sizeof (buf), "DATA\r\n"); + if (mutt_socket_write (conn, buf) == -1) + { + fclose (fp); + return smtp_err_write; + } + if ((r = smtp_get_resp (conn))) + { + fclose (fp); + return r; + } + + while (fgets (buf, sizeof (buf), fp)) + { + if (buf[0] == '.') + { + if (mutt_socket_write_d (conn, ".", 3) == -1) + { + fclose (fp); + return smtp_err_write; + } + } + if (mutt_socket_write_d (conn, buf, 3) == -1) + { + fclose (fp); + return smtp_err_write; + } + + mutt_progress_bar (&progress, ftell (fp)); + } + 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; +} + +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; + int ret = -1; + char buf[1024]; + + 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>", from->mailbox); + 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); + 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")); + + return ret; +} + +static int smtp_fill_account (ACCOUNT* account) +{ + static unsigned short SmtpPort = 0; + + struct servent* service; + ciss_url_t url; + char* urlstr; + + 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->flags = 0; + account->port = SmtpPort; + account->type = M_ACCT_TYPE_SMTP; + + urlstr = safe_strdup (SmtpUrl); + url_parse_ciss (&url, urlstr); + if (url.scheme != U_SMTP || mutt_account_fromurl (account, &url) < 0) + { + FREE (&urlstr); + mutt_error (_("Invalid SMTP URL: %s"), SmtpUrl); + mutt_sleep (1); + return -1; + } + + FREE (&urlstr); + return 0; +} + +static int smtp_helo (CONNECTION* conn) +{ + char buf[LONG_STRING]; + + memset (Capabilities, 0, sizeof (Capabilities)); + FREE (&AuthMechs); + + if (!Esmtp) + { + /* if TLS or AUTH are requested, use EHLO */ + if (conn->account.flags & M_ACCT_USER) + Esmtp = 1; +#if defined(USE_SSL) || defined(USE_GNUTLS) + if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != M_NO) + Esmtp = 1; +#endif + } + + 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; + +#if defined(USE_SSL) || defined(USE_GNUTLS) + if (option (OPTSSLFORCETLS)) + rc = M_YES; + else if (mutt_bit_isset (Capabilities, STARTTLS) && + (rc = query_quadoption (OPT_SSLSTARTTLS, + _("Secure connection with TLS?"))) == -1) + return rc; + + if (rc == M_YES) + { + if (mutt_socket_write (conn, "STARTTLS\r\n") < 0) + return smtp_err_write; + if ((rc = smtp_get_resp (conn))) + return rc; + +#ifdef USE_SSL + if (mutt_ssl_starttls (conn)) +#elif USE_GNUTLS + if (mutt_gnutls_starttls (idata->conn)) +#endif + { + 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 + + if (conn->account.flags & M_ACCT_USER) + { + if (!mutt_bit_isset (Capabilities, AUTH)) + { + mutt_error (_("SMTP server does not support authentication")); + mutt_sleep (1); + return -1; + } + +#ifdef USE_SASL + return smtp_auth (conn); +#else + mutt_error (_("SMTP authentication requires SASL")); + mutt_sleep (1); + return -1; +#endif /* USE_SASL */ + } + + return 0; +} + +#ifdef USE_SASL +static int smtp_auth (CONNECTION* conn) +{ + int r = SMTP_AUTH_UNAVAIL; + + if (SmtpAuthenticators && *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 ((r = smtp_auth_sasl (conn, method)) != SMTP_AUTH_UNAVAIL) + break; + } + + FREE (&methods); + } + else + r = smtp_auth_sasl (conn, SmtpAuthenticators); + + if (r == SMTP_AUTH_UNAVAIL) + { + mutt_error (_("No authenticators available")); + mutt_sleep (1); + } + + return r == SMTP_AUTH_SUCCESS ? 0 : -1; +} + +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 len; + char buf[HUGE_STRING]; + int rc, saslrc; + + if (mutt_sasl_client_new (conn, &saslconn) < 0) + return SMTP_AUTH_FAIL; + + do + { + rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech); + if (rc == SASL_INTERACT) + mutt_sasl_interact (interaction); + } + while (rc == SASL_INTERACT); + + if (rc != SASL_OK && rc != SASL_CONTINUE) + { + dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech)); + sasl_dispose (&saslconn); + return SMTP_AUTH_UNAVAIL; + } + + mutt_message (_("Authenticating (%s)..."), mech); + + snprintf (buf, sizeof (buf), "AUTH %s", mech); + if (len) + { + strncat (buf, " ", sizeof (buf)); + if (sasl_encode64 (data, len, buf + mutt_strlen (buf), + sizeof (buf) - mutt_strlen (buf), &len) != SASL_OK) + { + dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); + goto fail; + } + } + strncat (buf, "\r\n", sizeof (buf)); + + do { + if (mutt_socket_write (conn, buf) < 0) + goto fail; + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + goto fail; + rc = atoi(buf); + + if (rc != smtp_ready) + break; + + if (sasl_decode64 (buf+4, strlen (buf+4), buf, sizeof (buf), &len) != SASL_OK) + { + dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n")); + goto fail; + } + + do + { + saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len); + if (saslrc == SASL_INTERACT) + mutt_sasl_interact (interaction); + } + while (saslrc == SASL_INTERACT); + + if (len) + { + if (sasl_encode64 (data, len, buf, sizeof (buf), &len) != SASL_OK) + { + dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); + goto fail; + } + } + strfcpy (buf + len, "\r\n", sizeof (buf) - len); + } while (rc == smtp_ready); + + if (smtp_success (rc)) + { + mutt_sasl_setup_conn (conn, saslconn); + return SMTP_AUTH_SUCCESS; + } + +fail: + mutt_error (_("SASL authentication failed")); + mutt_sleep (1); + sasl_dispose (&saslconn); + return SMTP_AUTH_FAIL; +} +#endif /* USE_SASL */
Attachment:
pgpgNn7lRoxyz.pgp
Description: PGP signature