]> granicus.if.org Git - mutt/commitdiff
This patch adds ESMTP relay support to mutt.
authorBrendan Cully <brendan@kublai.com>
Sat, 24 Feb 2007 06:37:31 +0000 (06:37 +0000)
committerBrendan Cully <brendan@kublai.com>
Sat, 24 Feb 2007 06:37:31 +0000 (06:37 +0000)
To use, set $smtp_url to the address of your smtp relay, in the form:

smtp[s]://[user[:password]@]host[:port]/

where port defaults to 25 for smtp and 465 for smtps.

You can also set $smtp_authenticators to control which methods mutt
will attempt to use during authentication. See $imap_authenticators
for details.

14 files changed:
Makefile.am
account.c
account.h
configure.in
globals.h
init.h
main.c
mutt_sasl.c
protos.h
send.c
sendlib.c
smtp.c [new file with mode: 0644]
url.c
url.h

index 46ada466b3a23c890341d0b0c4626cbf18f9aa3d..5b25088bcc470a2b4f057ba6a994b795335986d8 100644 (file)
@@ -60,7 +60,7 @@ CPPFLAGS=@CPPFLAGS@ -I$(includedir)
 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.h hcache.c bcache.c bcache.h mutt_ssl_gnutls.c \
        crypt-gpgme.c crypt-mod-pgp-gpgme.c crypt-mod-smime-gpgme.c \
index d53d304d6cbf457d5d72d02881a53dfd5de04fa4..c5cd33ee8b3bab5edf55db5d507e3a64f27bbbf2 100644 (file)
--- a/account.c
+++ b/account.c
@@ -123,6 +123,16 @@ void mutt_account_tourl (ACCOUNT* account, ciss_url_t* url)
   }
 #endif
 
+#ifdef USE_SMTP
+  if (account->type == M_ACCT_TYPE_SMTP)
+  {
+    if (account->flags & M_ACCT_SSL)
+      url->scheme = U_SMTPS;
+    else
+      url->scheme = U_SMTP;
+  }
+#endif
+
   url->host = account->host;
   if (account->flags & M_ACCT_PORT)
     url->port = account->port;
index ce1ebb760211055500c4ebd36e2d7ebb4fb032c6..249ef1a9f275282df169aa9c118ae7318f44ddf6 100644 (file)
--- a/account.h
+++ b/account.h
@@ -28,7 +28,8 @@ enum
 {
   M_ACCT_TYPE_NONE = 0,
   M_ACCT_TYPE_IMAP,
-  M_ACCT_TYPE_POP
+  M_ACCT_TYPE_POP,
+  M_ACCT_TYPE_SMTP
 };
 
 /* account flags */
index 8fa03858c6d973650b02d53c8e81345c5cb8d731..57eba4f73edae2eb68d84819febdd54ab786dd33 100644 (file)
@@ -573,6 +573,13 @@ AC_ARG_ENABLE(imap, AC_HELP_STRING([--enable-imap], [Enable IMAP support]),
 ])
 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])
+
 if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
   MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
 fi
index 7836155b062eb1cb81f4d2ec8b21e6edac450cb0..5ac21e297ae2c055d86220712588b8b673d909ad 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -115,6 +115,10 @@ WHERE char *Sendmail;
 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)
diff --git a/init.h b/init.h
index 0bba9d226c10d347f6eccb30f8a63b1108ea0e19..377844b0add6fd10b992c4c6a9a3cbcdf0ddad2b 100644 (file)
--- a/init.h
+++ b/init.h
@@ -92,6 +92,9 @@ struct option_t
 # ifndef USE_POP
 #  define USE_POP
 # endif
+# ifndef USE_SMTP
+#  define USE_SMTP
+# endif
 # ifndef USE_SSL_OPENSSL
 #  define USE_SSL_OPENSSL
 # endif
@@ -2550,6 +2553,34 @@ struct option_t MuttVars[] = {
   ** 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, UL 0 },
+  /*
+  ** .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[s]://[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 smtp_authenticators="digest-md5:cram-md5"
+   */
+# endif /* USE_SASL */
+#endif /* USE_SMTP */
   { "sort",            DT_SORT, R_INDEX|R_RESORT, UL &Sort, SORT_DATE },
   /*
   ** .pp
@@ -2945,7 +2976,10 @@ struct option_t MuttVars[] = {
   /*
   ** .pp
   ** Controls whether mutt writes out the Bcc header when preparing
-  ** messages to be sent.  Exim users may wish to unset this.
+  ** messages to be sent.  Exim users may wish to unset this. If mutt
+  ** is set to deliver directly via SMTP (see ``$$smtp_url''), this
+  ** option does nothing: mutt will never write out the BCC header
+  ** in this case.
   */
   /*--*/
   { NULL }
diff --git a/main.c b/main.c
index 0ac2f00132f1c3c9a5d358b084c6fea0d9f6d564..f3589ccece4b3c8174c03cf9e5eae6ddfd5651bd 100644 (file)
--- a/main.c
+++ b/main.c
@@ -257,6 +257,12 @@ static void show_version (void)
         "-USE_IMAP  "
 #endif
 
+#ifdef USE_SMTP
+       "+USE_SMTP  "
+#else
+       "-USE_SMTP  "
+#endif
+
 #ifdef USE_GSS
        "+USE_GSS  "
 #else
index 21fa0633818bf0211f0b9a6db653770a746df111..95da33d3f95f0d5e665131ee740c4f85379b874b 100644 (file)
@@ -182,42 +182,44 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
     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"));
+    mutt_error (_("SASL failed to parse remote IP address"));
     return -1;
   }
 
-  dprint(1,(debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport));
+  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;
   }
 
@@ -227,8 +229,7 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
   secprops.maxbufsize = M_SASL_MAXBUF;
   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;
   }
 
@@ -238,13 +239,13 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
     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;
     }
   }
index baec526bb430aa4c9974b9135bb06651124a67fb..b74041ba1d3460bb727f5b41c3c422518ed477b3 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -340,6 +340,10 @@ int mutt_save_attachment (FILE *, BODY *, char *, int, HEADER *);
 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 --git a/send.c b/send.c
index 9b35e194d7eaa8865c7ef9c42bb367359dfbb1ba..0b4b8eb95980adfecd876aafb7b2ac562bb1efe7 100644 (file)
--- a/send.c
+++ b/send.c
@@ -960,18 +960,30 @@ static int send_message (HEADER *msg)
   char tempfile[_POSIX_PATH_MAX];
   FILE *tempfp;
   int i;
+#ifdef USE_SMTP
+  short old_write_bcc;
+#endif
   
   /* Write out the message in MIME form. */
   mutt_mktemp (tempfile);
   if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
     return (-1);
 
+#ifdef USE_SMTP
+  old_write_bcc = option (OPTWRITEBCC);
+  if (SmtpUrl)
+    unset_option (OPTWRITEBCC);
+#endif
 #ifdef MIXMASTER
   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0, msg->chain ? 1 : 0);
 #endif
 #ifndef MIXMASTER
   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0, 0);
 #endif
+#ifdef USE_SMTP
+  if (old_write_bcc)
+    set_option (OPTWRITEBCC);
+#endif
   
   fputc ('\n', tempfp); /* tie off the header. */
 
@@ -994,8 +1006,16 @@ static int send_message (HEADER *msg)
     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);
 }
 
index 13b0392058bf58092e3a9ead190321b3e547104a..dcfbc5c7182b7ea31614bba0064457e1c5152e44 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -2361,6 +2361,12 @@ static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *r
     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);
   }
@@ -2611,7 +2617,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
     rewind (tempfp);
     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
       lines++;
-    fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", ftello (tempfp));
+    fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftell (tempfp));
     fprintf (msg->fp, "Lines: %d\n\n", lines);
 
     /* copy the body and clean up */
diff --git a/smtp.c b/smtp.c
new file mode 100644 (file)
index 0000000..2aaac35
--- /dev/null
+++ b/smtp.c
@@ -0,0 +1,553 @@
+/* mutt - text oriented MIME mail user agent
+ * Copyright (C) 2002 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2005-7 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
+#include "mutt_sasl.h"
+
+#include <sasl/sasl.h>
+#include <sasl/saslutil.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_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,
+
+  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);
+      FREE (&AuthMechs);
+      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;
+  size_t buflen;
+
+  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..."), M_PROGRESS_SIZE,
+                      NetInc, st.st_size);
+
+  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) - 1, fp))
+  {
+    buflen = mutt_strlen (buf);
+    if (buflen && buf[buflen-1] == '\n'
+       && (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, M_SOCK_LOG_FULL) == -1)
+      {
+        fclose (fp);
+        return smtp_err_write;
+      }
+    }
+    if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) == -1)
+    {
+      fclose (fp);
+      return smtp_err_write;
+    }
+
+    mutt_progress_update (&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>",
+                    EnvFrom ? EnvFrom->mailbox : 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;
+
+  account->flags = 0;
+  account->port = 0;
+  account->type = M_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 |= M_ACCT_SSL;
+
+  if (!account->port)
+  {
+    if (account->flags & M_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];
+
+  memset (Capabilities, 0, sizeof (Capabilities));
+
+  if (!Esmtp)
+  {
+    /* if TLS or AUTH are requested, use EHLO */
+    if (conn->account.flags & M_ACCT_USER)
+      Esmtp = 1;
+#ifdef USE_SSL
+    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;
+
+#ifdef USE_SSL
+  if (conn->ssf)
+    rc = M_NO;
+  else 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;
+
+    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
+
+  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, AuthMechs);
+
+  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)
+  {
+    safe_strcat (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;
+    }
+  }
+  safe_strcat (buf, sizeof (buf), "\r\n");
+
+  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;
+  }
+  else if (SmtpAuthenticators && *SmtpAuthenticators)
+  {
+    /* if we're given a mech list to attempt, failure means try the next */
+    dprint (2, (debugfile, "smtp_auth_sasl: %s failed\n", mech));
+    sasl_dispose (&saslconn);
+    return SMTP_AUTH_UNAVAIL;
+  }
+
+fail:
+  mutt_error (_("SASL authentication failed"));
+  mutt_sleep (1);
+  sasl_dispose (&saslconn);
+  return SMTP_AUTH_FAIL;
+}
+#endif /* USE_SASL */
diff --git a/url.c b/url.c
index 7b9b228940c29767821888391e22bfe1406220a8..3e3c5a33ce24a0e3b35cb00c9446ce9f31f7a6b9 100644 (file)
--- a/url.c
+++ b/url.c
@@ -37,10 +37,12 @@ static struct mapping_t UrlMap[] =
   { "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 },
+  { "smtps",    U_SMTPS },
+  { NULL,      U_UNKNOWN }
 };
 
 
diff --git a/url.h b/url.h
index 0bcc4bdc487f0c2f47d10c63b11804c644ce4ec7..cf9b4b462d1705973dea96c72bf9a0c8b4158f25 100644 (file)
--- a/url.h
+++ b/url.h
@@ -8,6 +8,8 @@ typedef enum url_scheme
   U_POPS,
   U_IMAP,
   U_IMAPS,
+  U_SMTP,
+  U_SMTPS,
   U_MAILTO,
   U_UNKNOWN
 }