commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-98-g89ea742


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-98-g89ea742
Date: Mon, 20 Sep 2010 21:35:57 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=89ea7423fc240601e018851dbb219a07d476e1d4

The branch, master has been updated
       via  89ea7423fc240601e018851dbb219a07d476e1d4 (commit)
      from  7b5902b4ffd7acf44934dcdd1d09e70abe26efc3 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 89ea7423fc240601e018851dbb219a07d476e1d4
Author: Sergey Poznyakoff <address@hidden>
Date:   Tue Sep 21 00:36:10 2010 +0300

    Rewrite SMTP mailer.
    
    * libproto/mailer/smtp.c: Rewrite using new SMTP API.
    * libproto/mailer/smtp_quit.c (mu_smtp_quit): Return immediately
    if already in closed state.
    
    * libmailutils/ticket.c (mu_ticket_get_cred): Return MU_ERR_FAILURE
    if all methods fail.
    * mail/send.c: Port 23321cf7 from patches-2.2
    (msg_to_pipe): Return status code.
    (save_dead_message, send_message): New functions, extracted from
    mail_send0.
    (mail_send0): Call save_dead_message if sending failed.

-----------------------------------------------------------------------

Summary of changes:
 libmailutils/ticket.c       |    4 +-
 libproto/mailer/smtp.c      | 1512 ++++++++-----------------------------------
 libproto/mailer/smtp_quit.c |    2 +
 mail/send.c                 |  219 ++++---
 4 files changed, 387 insertions(+), 1350 deletions(-)

diff --git a/libmailutils/ticket.c b/libmailutils/ticket.c
index ae49520..ec7f609 100644
--- a/libmailutils/ticket.c
+++ b/libmailutils/ticket.c
@@ -211,7 +211,9 @@ mu_ticket_get_cred (mu_ticket_t ticket, mu_url_t url, const 
char *challenge,
        }
       arg [strlen (arg) - 1] = '\0'; /* nuke the trailing line.  */
     }
-
+  else
+    return MU_ERR_FAILURE;
+  
   if (pplain)
     {
       *pplain = strdup (arg);
diff --git a/libproto/mailer/smtp.c b/libproto/mailer/smtp.c
index 31b725f..e3da784 100644
--- a/libproto/mailer/smtp.c
+++ b/libproto/mailer/smtp.c
@@ -37,24 +37,21 @@
 #include <unistd.h>
 
 #include <mailutils/address.h>
+#include <mailutils/argcv.h>
 #include <mailutils/debug.h>
 #include <mailutils/errno.h>
 #include <mailutils/header.h>
 #include <mailutils/body.h>
+#include <mailutils/iterator.h>
 #include <mailutils/message.h>
 #include <mailutils/mime.h>
 #include <mailutils/mutil.h>
 #include <mailutils/observer.h>
 #include <mailutils/property.h>
 #include <mailutils/stream.h>
-#include <mailutils/url.h>
+#include <mailutils/smtp.h>
 #include <mailutils/tls.h>
-#include <mailutils/md5.h>
-#include <mailutils/io.h>
-#include <mailutils/secret.h>
-#include <mailutils/cctype.h>
 #include <mailutils/cstr.h>
-
 #include <mailutils/sys/mailer.h>
 #include <mailutils/sys/url.h>
 #include <mailutils/sys/registrar.h>
@@ -100,1071 +97,149 @@ static struct _mu_record _smtp_record = {
    the mailbox, via the register entry/record.  */
 mu_record_t     mu_smtp_record = &_smtp_record;
 
-struct _smtp
+struct _smtp_mailer
 {
   mu_mailer_t     mailer;
-  char           *mailhost;
-  char           *localhost;
-
-  /* IO buffering. */
-  char           *buffer;      /* Must be freed. */
-  size_t          buflen;
-
-  char           *ptr;
-  char           *nl;
-
-  enum smtp_state
-  {
-    SMTP_NO_STATE, SMTP_OPEN, SMTP_GREETINGS, SMTP_EHLO, SMTP_EHLO_ACK,
-    SMTP_HELO, SMTP_HELO_ACK, SMTP_QUIT, SMTP_QUIT_ACK, SMTP_ENV_FROM,
-    SMTP_ENV_RCPT, SMTP_MAIL_FROM, SMTP_MAIL_FROM_ACK, SMTP_RCPT_TO,
-    SMTP_RCPT_TO_ACK, SMTP_DATA, SMTP_DATA_ACK, SMTP_SEND, SMTP_SEND_ACK,
-    SMTP_SEND_DOT, SMTP_STARTTLS, SMTP_STARTTLS_ACK, SMTP_AUTH, SMTP_AUTH_ACK,
-  }
-  state;
-
-  int             extended;
-  unsigned long   capa;                /* Server capabilities */
-  size_t          max_size;    /* Maximum message size the server is willing
-                                  to accept */
-  unsigned long   auth_mechs;  /* Available ESMTP AUTH mechanisms */
+  mu_smtp_t       smtp;
 
-  const char     *mail_from;
-  mu_address_t    rcpt_to;     /* Destroy this if not the same as argto below. 
*/
+  mu_address_t    rcpt_to;
   mu_address_t    rcpt_bcc;
-  size_t          rcpt_to_count;
-  size_t          rcpt_bcc_count;
-  size_t          rcpt_index;
-  size_t          rcpt_count;
-  int             bccing;
-  mu_message_t    msg;         /* Destroy this if not same argmsg. */
-
-  /* The mu_mailer_send_message() args. */
-  mu_message_t    argmsg;
-  mu_address_t    argfrom;
-  mu_address_t    argto;
 };
 
-typedef struct _smtp *smtp_t;
-
-/* ESMTP capabilities */
-#define CAPA_STARTTLS        0x00000001
-#define CAPA_8BITMIME        0x00000002
-#define CAPA_SIZE            0x00000004
-#define CAPA_AUTH            0x00000008
-
-/* ESMTP AUTH mechanisms */
-#define AUTH_LOGIN           0x00000001
-#define AUTH_PLAIN           0x00000002
-#define AUTH_CRAM_MD5        0x00000004
-#define AUTH_DIGEST_MD5      0x00000008
-#define AUTH_GSSAPI          0x00000010
-#define AUTH_EXTERNAL        0x00000020
-
-struct auth_mech_record
-{
-  unsigned long   id;
-  char           *name;
-};
-
-static struct auth_mech_record auth_mech_list[] = {
-  {AUTH_LOGIN, "login"},
-  {AUTH_PLAIN, "plain"},
-  {AUTH_CRAM_MD5, "cram-md5"},
-  {AUTH_DIGEST_MD5, "digest-md5"},
-  {AUTH_GSSAPI, "gssapi"},
-  {AUTH_EXTERNAL, "external"},
-  {0, NULL},
-};
-
-static void     smtp_destroy (mu_mailer_t);
-static int      smtp_open (mu_mailer_t, int);
-static int      smtp_close (mu_mailer_t);
-static int      smtp_send_message (mu_mailer_t, mu_message_t, mu_address_t,
-                                  mu_address_t);
-static int      smtp_writeline (smtp_t smtp, const char *format, ...);
-static int      smtp_readline (smtp_t);
-static int      smtp_read_ack (smtp_t);
-static int      smtp_parse_ehlo_ack (smtp_t);
-static int      smtp_write (smtp_t);
-static int      smtp_starttls (smtp_t);
-static int      smtp_auth (smtp_t);
-
-static int      _smtp_set_rcpt (smtp_t, mu_message_t, mu_address_t);
-
-/* Useful little macros, since these are very repetitive. */
-
-static void
-CLEAR_STATE (smtp_t smtp)
-{
-  smtp->ptr = smtp->buffer;
-  smtp->nl = NULL;
-
-  smtp->state = SMTP_NO_STATE;
-
-  smtp->extended = 0;
-
-  if (smtp->mail_from)
-    smtp->mail_from = NULL;
-
-  if (smtp->rcpt_to != smtp->argto)
-    mu_address_destroy (&smtp->rcpt_to);
-
-  smtp->rcpt_to = NULL;
-
-  mu_address_destroy (&smtp->rcpt_bcc);
-
-  smtp->rcpt_to_count = 0;
-  smtp->rcpt_bcc_count = 0;
-  smtp->rcpt_index = 0;
-  smtp->rcpt_count = 0;
-  smtp->bccing = 0;
-
-  if (smtp->msg != smtp->argmsg)
-    mu_message_destroy (&smtp->msg, NULL);
-
-  smtp->msg = NULL;
-
-  smtp->argmsg = NULL;
-  smtp->argfrom = NULL;
-  smtp->argto = NULL;
-}
-
-/* If we are resuming, we should be resuming the SAME operation
-   as that which is ongoing. Check this. */
-static int
-smtp_check_send_resumption (smtp_t smtp,
-                           mu_message_t msg, mu_address_t from,
-                           mu_address_t to)
-{
-  if (smtp->state == SMTP_NO_STATE)
-    return 0;
-
-  /* FIXME: state should be one of the "send" states if its not
-     "no state" */
-  if (msg != smtp->argmsg)
-    return MU_ERR_BAD_RESUMPTION;
-
-  if (from != smtp->argfrom)
-    return MU_ERR_BAD_RESUMPTION;
-
-  if (to != smtp->argto)
-    return MU_ERR_BAD_RESUMPTION;
-
-  return 0;
-}
-
-#define CHECK_SEND_RESUME(smtp, msg, from, to) \
-do { \
-  if((status = smtp_check_send_resumption(smtp, msg, from, to)) != 0) \
-    return status; \
-} while (0)
-
-/* Clear the state and close the stream. */
-#define CHECK_ERROR_CLOSE(mailer, smtp, status) \
-do \
-  { \
-     if (status != 0) \
-       { \
-          mu_stream_close (mailer->stream); \
-          CLEAR_STATE (smtp); \
-          return status; \
-       } \
-  } \
-while (0)
-
-/* Clear the state. */
-#define CHECK_ERROR(smtp, status) \
-do \
-  { \
-     if (status != 0) \
-       { \
-          CLEAR_STATE (smtp); \
-          return status; \
-       } \
-  } \
-while (0)
-
-/* Clear the state for non recoverable error.  */
-#define CHECK_EAGAIN(smtp, status) \
-do \
-  { \
-    if (status != 0) \
-      { \
-         if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
-           { \
-             CLEAR_STATE (smtp); \
-           } \
-         return status; \
-      } \
-   }  \
-while (0)
-
-static int
-_mailer_smtp_init (mu_mailer_t mailer)
-{
-  smtp_t          smtp;
-
-  /* Allocate memory specific to smtp mailer.  */
-  smtp = mailer->data = calloc (1, sizeof (*smtp));
-  if (mailer->data == NULL)
-    return ENOMEM;
-
-  smtp->mailer = mailer;       /* Back pointer.  */
-  smtp->state = SMTP_NO_STATE;
-
-  mailer->_destroy = smtp_destroy;
-  mailer->_open = smtp_open;
-  mailer->_close = smtp_close;
-  mailer->_send_message = smtp_send_message;
-
-  /* Set our properties.  */
-  {
-    mu_property_t   property = NULL;
-
-    mu_mailer_get_property (mailer, &property);
-    mu_property_set_value (property, "TYPE", "SMTP", 1);
-  }
-
-  return 0;
-}
-
-static void
-smtp_destroy (mu_mailer_t mailer)
-{
-  smtp_t          smtp = mailer->data;
-
-  CLEAR_STATE (smtp);
-
-  /* Not our responsability to close.  */
-
-  if (smtp->mailhost)
-    free (smtp->mailhost);
-  if (smtp->localhost)
-    free (smtp->localhost);
-  if (smtp->buffer)
-    free (smtp->buffer);
-
-  free (smtp);
-
-  mailer->data = NULL;
-}
-
-/** Open an SMTP mailer.
-An SMTP mailer must be opened before any messages can be sent.
address@hidden mailer the mailer created by smtp_create()
address@hidden flags the mailer flags
-*/
 static int
 smtp_open (mu_mailer_t mailer, int flags)
 {
-  smtp_t          smtp = mailer->data;
-  int             status;
-  long            port;
-
-  /* Sanity checks.  */
-  if (!smtp)
-    return EINVAL;
+  const char *host;
+  long port;
+  struct _smtp_mailer *smtp_mailer = mailer->data;
+  int rc;
+  size_t parmc = 0;
+  char **parmv = NULL;
+  int notls = 0;
+  int noauth = 0;
+  
+  rc = mu_smtp_create (&smtp_mailer->smtp);
+  if (rc)
+    return rc;
+  if (mu_debug_check_level (mailer->debug, MU_DEBUG_PROT))
+    mu_smtp_trace (smtp_mailer->smtp, MU_SMTP_TRACE_SET);
+  if (mu_debug_check_level (mailer->debug, MU_DEBUG_TRACE6))
+    mu_smtp_trace_mask (smtp_mailer->smtp, MU_SMTP_TRACE_SET, 
MU_XSCRIPT_SECURE);
+  if (mu_debug_check_level (mailer->debug, MU_DEBUG_TRACE7))
+    mu_smtp_trace_mask (smtp_mailer->smtp, MU_SMTP_TRACE_SET, 
MU_XSCRIPT_PAYLOAD);
+  
+  mu_smtp_set_param (smtp_mailer->smtp, MU_SMTP_PARAM_URL,
+                    mu_url_to_string (mailer->url));
 
-  mailer->flags = flags;
+  rc = mu_url_sget_host (mailer->url, &host);
+  if (rc)
+    return rc;
+  if (mu_url_get_port (mailer->url, &port))
+    port = 25;
 
-  if ((status = mu_url_get_port (mailer->url, &port)) != 0)
-    return status;
-
-  switch (smtp->state)
+  /* Additional information is supplied in the arguments */
+  if (mu_url_sget_fvpairs (mailer->url, &parmc, &parmv) == 0)
     {
-    case SMTP_NO_STATE:
-      if (smtp->mailhost)
-       {
-         free (smtp->mailhost);
-         smtp->mailhost = NULL;
-       }
-
-      /* Fetch the mailer server name and the port in the mu_url_t.  */
-      if ((status = mu_url_aget_host (mailer->url, &smtp->mailhost)) != 0)
-       return status;
-
-      if (smtp->localhost)
-       {
-         free (smtp->localhost);
-         smtp->localhost = NULL;
-       }
-      /* Fetch our local host name.  */
-
-      status = mu_get_host_name (&smtp->localhost);
-
-      if (status != 0)
-       {
-         /* gethostname failed, abort.  */
-         free (smtp->mailhost);
-         smtp->mailhost = NULL;
-         return status;
-       }
-
-      /* allocate a working io buffer.  */
-      if (smtp->buffer == NULL)
-       {
-         smtp->buflen = 512;   /* Initial guess.  */
-         smtp->buffer = malloc (smtp->buflen + 1);
-         if (smtp->buffer == NULL)
-           {
-             CHECK_ERROR (smtp, ENOMEM);
-           }
-         smtp->ptr = smtp->buffer;
-       }
-
-      /* Create a TCP stack if one is not given.  */
-      if (mailer->stream == NULL)
-       {
-         status =
-           mu_tcp_stream_create (&mailer->stream, smtp->mailhost, port,
-                                 mailer->flags);
-         CHECK_ERROR (smtp, status);
-         mu_stream_set_buffer (mailer->stream, mu_buffer_line, BUFSIZ);
-       }
-      CHECK_ERROR (smtp, status);
-      smtp->state = SMTP_OPEN;
-
-    case SMTP_OPEN:
-      MU_DEBUG2 (mailer->debug, MU_DEBUG_PROT,
-                "smtp_open (host: %s port: %ld)\n", smtp->mailhost, port);
-      status = mu_stream_open (mailer->stream);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_GREETINGS;
-
-    case SMTP_GREETINGS:
-      /* Swallow the greetings.  */
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-
-      if (smtp->buffer[0] != '2')
-       {
-         mu_stream_close (mailer->stream);
-         return EACCES;
-       }
-
-    ehlo:
-      status = smtp_writeline (smtp, "EHLO %s\r\n", smtp->localhost);
-      CHECK_ERROR (smtp, status);
-
-      smtp->state = SMTP_EHLO;
-
-    case SMTP_EHLO:
-      /* We first try Extended SMTP.  */
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_EHLO_ACK;
-
-    case SMTP_EHLO_ACK:
-      status = smtp_parse_ehlo_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-
-      if (smtp->buffer[0] != '2')
-       {
-         smtp->extended = 0;
-         status = smtp_writeline (smtp, "HELO %s\r\n", smtp->localhost);
-         CHECK_ERROR (smtp, status);
-         smtp->state = SMTP_HELO;
-       }
-      else
-       {
-         smtp->extended = 1;
-
-         if (smtp->capa & CAPA_STARTTLS)
-           smtp->state = SMTP_STARTTLS;
-         else if (smtp->capa & CAPA_AUTH && mailer->url->user)
-           {
-             smtp->state = SMTP_AUTH;
-           }
-         else
-           break;
-       }
-
-    case SMTP_STARTTLS:
-    case SMTP_STARTTLS_ACK:
-      if ((smtp->capa & CAPA_STARTTLS) && smtp_starttls (smtp) == 0)
-       goto ehlo;
+      size_t i;
       
-    case SMTP_AUTH:
-    case SMTP_AUTH_ACK:
-      if (smtp->capa & CAPA_AUTH)
-       {
-         smtp_auth (smtp);
-         break;
-       }
-
-    case SMTP_HELO:
-      if (!smtp->extended)     /* FIXME: this will always be false! */
-       {
-         status = smtp_write (smtp);
-         CHECK_EAGAIN (smtp, status);
-       }
-      smtp->state = SMTP_HELO_ACK;
-
-    case SMTP_HELO_ACK:
-      if (!smtp->extended)
+      for (i = 0; i < parmc; i++)
        {
-         status = smtp_read_ack (smtp);
-         CHECK_EAGAIN (smtp, status);
-
-         if (smtp->buffer[0] != '2')
+         if (strcmp (parmv[i], "notls") == 0)
+           notls = 1;
+         else if (strcmp (parmv[i], "noauth") == 0)
+           noauth = 1;
+         else if (strncmp (parmv[i], "auth=", 5) == 0)
            {
-             mu_stream_close (mailer->stream);
-             CLEAR_STATE (smtp);
-             return EACCES;
+             int mc, j;
+             char **mv;
+         
+             rc = mu_argcv_get_np (parmv[i] + 5, strlen (parmv[i] + 5),
+                                   ",", NULL,
+                                   0,
+                                   &mc, &mv, NULL);
+             if (rc == 0)
+               for (j = 0; j < mc; j++)
+                 mu_smtp_add_auth_mech (smtp_mailer->smtp, mv[j]);
+             
+             free (mv);
            }
+         /* unrecognized arguments silently ignored */
        }
-
-    default:
-      break;
     }
-
-  CLEAR_STATE (smtp);
-
-  return 0;
-}
-
-static int
-smtp_close (mu_mailer_t mailer)
-{
-  smtp_t          smtp = mailer->data;
-  int             status;
-
-  switch (smtp->state)
-    {
-    case SMTP_NO_STATE:
-      status = smtp_writeline (smtp, "QUIT\r\n");
-      CHECK_ERROR (smtp, status);
-
-      smtp->state = SMTP_QUIT;
-
-    case SMTP_QUIT:
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_QUIT_ACK;
-
-    case SMTP_QUIT_ACK:
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-
-    default:
-      break;
-    }
-  smtp->state = SMTP_NO_STATE;
-  return mu_stream_close (mailer->stream);
-}
-
-/*
-  Client side STARTTLS support.
- */
-
-static int
-smtp_starttls (smtp_t smtp)
-{
-#ifdef WITH_TLS
-  int             status;
-  mu_mailer_t     mailer = smtp->mailer;
-  mu_stream_t     newstr;
   
-  if (!mu_tls_enable || !(smtp->capa & CAPA_STARTTLS))
-    return -1;
-
-  smtp->capa = 0;
-  smtp->auth_mechs = 0;
-
-  status = smtp_writeline (smtp, "STARTTLS\r\n");
-  CHECK_ERROR (smtp, status);
-  status = smtp_write (smtp);
-  CHECK_EAGAIN (smtp, status);
-  status = smtp_read_ack (smtp);
-  CHECK_ERROR (smtp, status);
-  mu_stream_flush (mailer->stream);
-  status = mu_tls_client_stream_create (&newstr, mailer->stream,
-                                       mailer->stream, 0);
-  CHECK_ERROR (smtp, status);
-  status = mu_stream_open (newstr);
-  MU_DEBUG1 (mailer->debug, MU_DEBUG_PROT, "TLS negotiation %s\n",
-            status == 0 ? "succeeded" : "failed");
-  CHECK_ERROR (smtp, status);
-
-  mailer->stream = newstr;
-
-  return status;
-#else
-  return -1;
-#endif /* WITH_TLS */
-}
-
-static void
-cram_md5 (char *secret, unsigned char *challenge, size_t challenge_len, 
-          unsigned char *digest)
-{
-  struct mu_md5_ctx context;
-  unsigned char   ipad[64];
-  unsigned char   opad[64];
-  int             secret_len;
-  int             i;
-
-  if (secret == 0 || challenge == 0)
-    return;
-
-  secret_len = strlen (secret);
-  memset (ipad, 0, sizeof (ipad));
-  memset (opad, 0, sizeof (opad));
-
-  if (secret_len > 64)
-    {
-      mu_md5_init_ctx (&context);
-      mu_md5_process_bytes ((unsigned char *) secret, secret_len, &context);
-      mu_md5_finish_ctx (&context, ipad);
-      mu_md5_finish_ctx (&context, opad);
-    }
-  else
-    {
-      memcpy (ipad, secret, secret_len);
-      memcpy (opad, secret, secret_len);
-    }
-
-  for (i = 0; i < 64; i++)
-    {
-      ipad[i] ^= 0x36;
-      opad[i] ^= 0x5c;
-    }
-
-  mu_md5_init_ctx (&context);
-  mu_md5_process_bytes (ipad, sizeof (ipad), &context);
-  mu_md5_process_bytes (challenge, challenge_len, &context);
-  mu_md5_finish_ctx (&context, digest);
-
-  mu_md5_init_ctx (&context);
-  mu_md5_process_bytes (opad, sizeof (opad), &context);
-  mu_md5_process_bytes (digest, 16, &context);
-  mu_md5_finish_ctx (&context, digest);
-}
-
-static int
-smtp_auth (smtp_t smtp)
-{
-  int             status;
-  mu_mailer_t     mailer = smtp->mailer;
-  struct auth_mech_record *mechs = auth_mech_list;
-  const char     *chosen_mech_name = NULL;
-  int             chosen_mech_id = 0;
-
-  status = mu_url_sget_auth (mailer->url, &chosen_mech_name);
-  if (status != MU_ERR_NOENT)
+  if (mailer->stream == NULL)
     {
-      for (; mechs->name; mechs++)
-       {
-         if (!mu_c_strcasecmp (mechs->name, chosen_mech_name))
-           {
-             chosen_mech_id = mechs->id;
-             break;
-           }
-       }
+      rc = mu_tcp_stream_create (&mailer->stream, host, port, mailer->flags);
+      if (rc)
+       return rc;
+      mu_stream_set_buffer (mailer->stream, mu_buffer_line, 0);
+      rc = mu_stream_open (mailer->stream);
+      if (rc)
+       return rc;
     }
-  if (chosen_mech_id)
-    {
-      if (smtp->auth_mechs & chosen_mech_id)
-       {
-         smtp->auth_mechs = 0;
-         smtp->auth_mechs |= chosen_mech_id;
-       }
-      else
-       {
-         MU_DEBUG1 (mailer->debug, MU_DEBUG_ERROR,
-                    "mailer does not support AUTH '%s' mechanism\n",
-                    chosen_mech_name);
-         return -1;
-       }
-    }
-
-#if 0 && defined(WITH_GSASL)
+  mu_smtp_set_carrier (smtp_mailer->smtp, mailer->stream);
+  /* FIXME: Unref the stream */
 
-  /* FIXME: Add GNU SASL support. */
+  rc = mu_smtp_open (smtp_mailer->smtp);
+  if (rc)
+    return rc;
 
-#else
-
-  /* Provide basic AUTH mechanisms when GSASL is not enabled. */
+  rc = mu_smtp_ehlo (smtp_mailer->smtp);
+  if (rc)
+    return rc;
 
-  if (smtp->auth_mechs & AUTH_CRAM_MD5)
+  if (!notls && mu_tls_enable &&
+      mu_smtp_capa_test (smtp_mailer->smtp, "STARTTLS", NULL) == 0)
     {
-      int             i;
-      char           *p, *buf = NULL;
-      const char     *user = NULL;
-      mu_secret_t     secret;
-      unsigned char  *chl;
-      size_t          chlen, buflen = 0, b64buflen = 0;
-      unsigned char  *b64buf = NULL;
-      unsigned char   digest[16];
-      static char     ascii_digest[33];
-
-      memset (digest, 0, 16);
-
-      status = mu_url_sget_user (mailer->url, &user);
-      if (status == MU_ERR_NOENT)
-       return -1;
-
-      status = mu_url_get_secret (mailer->url, &secret);
-      if (status == MU_ERR_NOENT)
+      rc = mu_smtp_starttls (smtp_mailer->smtp);
+      if (rc == 0)
        {
-         MU_DEBUG (mailer->debug, MU_DEBUG_ERROR,
-                   "AUTH CRAM-MD5 mechanism requires giving a password\n");
-         return -1;
+         rc = mu_smtp_ehlo (smtp_mailer->smtp);
+         if (rc)
+           return rc;
        }
-
-      status = smtp_writeline (smtp, "AUTH CRAM-MD5\r\n");
-      CHECK_ERROR (smtp, status);
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-
-      if (strncmp (smtp->buffer, "334 ", 4))
-       {
-         MU_DEBUG (mailer->debug, MU_DEBUG_ERROR,
-                   "mailer rejected the AUTH CRAM-MD5 command\n");
-         return -1;
-       }
-
-      p = strchr (smtp->buffer, ' ') + 1;
-      mu_rtrim_cset (p, "\r\n");
-      mu_base64_decode ((unsigned char*) p, strlen (p), &chl, &chlen);
-
-      cram_md5 ((char *) mu_secret_password (secret), chl, chlen, digest);
-      mu_secret_password_unref (secret);
-      free (chl);
-
-      for (i = 0; i < 16; i++)
-       sprintf (ascii_digest + 2 * i, "%02x", digest[i]);
-
-      mu_asnprintf (&buf, &buflen, "%s %s", user, ascii_digest);
-      buflen = strlen (buf);
-      mu_base64_encode ((unsigned char*) buf, buflen, &b64buf, &b64buflen);
-      free (buf);
-
-      status = smtp_writeline (smtp, "%s\r\n", b64buf);
-      CHECK_ERROR (smtp, status);
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
     }
 
-  else if (smtp->auth_mechs & AUTH_PLAIN)
+  if (!noauth && mu_smtp_capa_test (smtp_mailer->smtp, "AUTH", NULL) == 0)
     {
-      int             c;
-      char           *buf = NULL;
-      unsigned char  *b64buf = NULL;
-      size_t          buflen = 0, b64buflen = 0;
-      const char     *user = NULL;
-      mu_secret_t     secret;
-
-      status = mu_url_sget_user (mailer->url, &user);
-      if (status == MU_ERR_NOENT)
-       return -1;
-
-      status = mu_url_get_secret (mailer->url, &secret);
-      if (status == MU_ERR_NOENT)
-       {
-         MU_DEBUG (mailer->debug, MU_DEBUG_ERROR,
-                   "AUTH PLAIN mechanism requires giving a password\n");
-         return -1;
-       }
-
-      mu_asnprintf (&buf, &buflen, "^%s^%s",
-                   user, mu_secret_password (secret));
-      mu_secret_password_unref (secret);
-      buflen = strlen (buf);
-      for (c = buflen - 1; c >= 0; c--)
-       {
-         if (buf[c] == '^')
-           buf[c] = '\0';
-       }
-      mu_base64_encode ((unsigned char*) buf, buflen, &b64buf, &b64buflen);
-      free (buf);
-
-      status = smtp_writeline (smtp, "AUTH PLAIN %s\r\n", b64buf);
-      CHECK_ERROR (smtp, status);
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-    }
-
-#endif /* not WITH_GSASL */
-  return 0;
-}
-
-static int
-message_set_header_value (mu_message_t msg, const char *field,
-                         const char *value)
-{
-  int             status = 0;
-  mu_header_t     hdr = NULL;
-
-  if ((status = mu_message_get_header (msg, &hdr)))
-    return status;
-
-  if ((status = mu_header_set_value (hdr, field, value, 1)))
-    return status;
-
-  return status;
-}
-
-static int
-message_has_bcc (mu_message_t msg)
-{
-  int             status;
-  mu_header_t     header = NULL;
-  size_t          bccsz = 0;
-
-  if ((status = mu_message_get_header (msg, &header)))
-    return status;
-
-  status = mu_header_get_value (header, MU_HEADER_BCC, NULL, 0, &bccsz);
-
-  /* MU_ERR_NOENT, or there was a Bcc: field. */
-  return status == MU_ERR_NOENT ? 0 : 1;
-}
-
-static int
-send_header (smtp_t smtp, mu_stream_t stream)
-{
-  int status;
-  int found_nl;
-  char data[256] = "";
-  size_t          n = 0;
-
-  while ((status = mu_stream_readline (stream, data, sizeof (data),
-                                      &n)) == 0 && n > 0)
-    {
-      int             nl;
-
-      found_nl = (n == 1 && data[0] == '\n');
-      if ((nl = (data[n - 1] == '\n')))
-       data[n - 1] = '\0';
-      if (data[0] == '.')
-       {
-         status = smtp_writeline (smtp, ".%s", data);
-         CHECK_ERROR (smtp, status);
-       }
-      else if (mu_c_strncasecmp (data, MU_HEADER_FCC,
-                                sizeof (MU_HEADER_FCC) - 1))
-       {
-         status = smtp_writeline (smtp, "%s", data);
-         CHECK_ERROR (smtp, status);
-         status = smtp_write (smtp);
-         CHECK_EAGAIN (smtp, status);
-       }
-      else
-       nl = 0;
-
-      if (nl)
-       {
-         status = smtp_writeline (smtp, "\r\n");
-         CHECK_ERROR (smtp, status);
-         status = smtp_write (smtp);
-         CHECK_EAGAIN (smtp, status);
-       }
-    }
-
-  if (!found_nl)
-    {
-      status = smtp_writeline (smtp, "\r\n");
-      CHECK_ERROR (smtp, status);
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
+      rc = mu_smtp_auth (smtp_mailer->smtp);
+      if (rc)
+       return rc;
+      rc = mu_smtp_ehlo (smtp_mailer->smtp);
+      if (rc)
+       return rc;
     }
+  
   return 0;
 }
 
-static int
-send_body (smtp_t smtp, mu_stream_t stream)
+static void
+smtp_destroy (mu_mailer_t mailer)
 {
-  int status;
-  char data[256] = "";
-  size_t          n = 0;
-
-  while ((status = mu_stream_readline (stream, data, sizeof (data) - 1,
-                                      &n)) == 0 && n > 0)
-    {
-      if (data[n - 1] == '\n')
-       data[n - 1] = '\0';
-      if (data[0] == '.')
-       status = smtp_writeline (smtp, ".%s\r\n", data);
-      else
-       status = smtp_writeline (smtp, "%s\r\n", data);
-      CHECK_ERROR (smtp, status);
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-    }
-
-  status = smtp_writeline (smtp, ".\r\n");
-  CHECK_ERROR (smtp, status);
-  smtp->state = SMTP_SEND_DOT;
-  return 0;
-}
+  struct _smtp_mailer *smp = mailer->data;
   
-/*
-
-The smtp mailer doesn't deal with mail like:
-
-To: address@hidden, address@hidden
-Bcc: address@hidden, address@hidden
-
-It just sends the message to all the addresses, making the
-"blind" cc not particularly blind.
-
-The correct algorithm is
-
-- open smtp connection
-- look as msg, figure out addrto&cc, and addrbcc
-- deliver to the to & cc addresses:
-  - if there are bcc addrs, remove the bcc field
-  - send the message to to & cc addrs:
-  mail from: me
-  rcpt to: address@hidden
-  rcpt to: address@hidden
-  data
-  ...
+  mu_address_destroy (&smp->rcpt_to);
+  mu_address_destroy (&smp->rcpt_bcc);
+  mu_smtp_destroy (&smp->smtp);
 
-- deliver to the bcc addrs:
+  free (smp);
 
-  for a in (bccaddrs)
-  do
-    - add header field to msg,  bcc: $a
-    - send the msg:
-    mail from: me
-    rcpt to: $a
-    data
-    ...
-  done
-
-- quit smtp connection
-
-*/
+  mailer->data = NULL;
+}
 
 static int
-smtp_send_message (mu_mailer_t mailer, mu_message_t argmsg,
-                  mu_address_t argfrom, mu_address_t argto)
+smtp_close (mu_mailer_t mailer)
 {
-  smtp_t          smtp = NULL;
-  int             status;
-
-  if (mailer == NULL)
-    return EINVAL;
-
-  smtp = mailer->data;
-  if (!smtp)
-    return EINVAL;
-
-  CHECK_SEND_RESUME (smtp, argmsg, argfrom, argto);
-
-  switch (smtp->state)
-    {
-    case SMTP_NO_STATE:
-      if (argmsg == NULL || argfrom == NULL)
-       return EINVAL;
-
-      smtp->argmsg = smtp->msg = argmsg;
-      smtp->argfrom = argfrom;
-      smtp->argto = argto;
-
-      status = mu_address_sget_email (smtp->argfrom, 1, &smtp->mail_from);
-      CHECK_ERROR (smtp, status);
-
-      status = _smtp_set_rcpt (smtp, smtp->argmsg, smtp->argto);
-      CHECK_ERROR (smtp, status);
-
-      /* Clear the Bcc: field if we found one. */
-      if (message_has_bcc (smtp->argmsg))
-       {
-         smtp->msg = NULL;
-         status = mu_message_create_copy (&smtp->msg, smtp->argmsg);
-         CHECK_ERROR (smtp, status);
-
-         status = message_set_header_value (smtp->msg, MU_HEADER_BCC, NULL);
-         CHECK_ERROR (smtp, status);
-       }
-
-      /* Begin bccing if there are not To: recipients. */
-      if (smtp->rcpt_to_count == 0)
-       smtp->bccing = 1;
-
-      smtp->rcpt_index = 1;
-
-      smtp->state = SMTP_ENV_FROM;
-
-    case SMTP_ENV_FROM:
-    ENV_FROM:
-      {
-       size_t          size;
-
-       if ((smtp->capa & CAPA_SIZE)
-           && mu_message_size (smtp->msg, &size) == 0)
-         status = smtp_writeline (smtp, "MAIL FROM:<%s> SIZE=%lu\r\n",
-                                  smtp->mail_from, size);
-       else
-         status = smtp_writeline (smtp, "MAIL FROM:<%s>\r\n",
-                                  smtp->mail_from);
-       CHECK_ERROR (smtp, status);
-       smtp->state = SMTP_MAIL_FROM;
-      }
-
-      /* We use a goto, since we may have multiple messages,
-         we come back here and doit all over again ... Not pretty.  */
-    case SMTP_MAIL_FROM:
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_MAIL_FROM_ACK;
-
-    case SMTP_MAIL_FROM_ACK:
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-      if (smtp->buffer[0] != '2')
-       {
-         mu_stream_close (mailer->stream);
-         CLEAR_STATE (smtp);
-         return EACCES;
-       }
-
-      /* We use a goto, since we may have multiple recipients,
-         we come back here and do it all over again ... Not pretty. */
-    case SMTP_ENV_RCPT:
-    ENV_RCPT:
-      {
-       mu_address_t    addr = smtp->rcpt_to;
-       const char     *to = NULL;
-
-       if (smtp->bccing)
-         addr = smtp->rcpt_bcc;
-       status = mu_address_sget_email (addr, smtp->rcpt_index, &to);
-
-       CHECK_ERROR (smtp, status);
-
-       /* Add the Bcc: field back in for recipient. */
-       if (smtp->bccing)
-         {
-           status = message_set_header_value (smtp->msg, MU_HEADER_BCC, to);
-           CHECK_ERROR (smtp, status);
-         }
-
-       status = smtp_writeline (smtp, "RCPT TO:<%s>\r\n", to);
-
-       CHECK_ERROR (smtp, status);
-
-       smtp->state = SMTP_RCPT_TO;
-       smtp->rcpt_index++;
-      }
-
-    case SMTP_RCPT_TO:
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_RCPT_TO_ACK;
-
-    case SMTP_RCPT_TO_ACK:
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-      if (smtp->buffer[0] != '2')
-       {
-         mu_stream_close (mailer->stream);
-         CLEAR_STATE (smtp);
-         return MU_ERR_SMTP_RCPT_FAILED;
-       }
-      /* Redo the receipt sequence for every To: and Cc: recipient. */
-      if (!smtp->bccing && smtp->rcpt_index <= smtp->rcpt_to_count)
-       goto ENV_RCPT;
-
-      /* We are done with the rcpt. */
-      status = smtp_writeline (smtp, "DATA\r\n");
-      CHECK_ERROR (smtp, status);
-      smtp->state = SMTP_DATA;
-
-    case SMTP_DATA:
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_DATA_ACK;
-
-    case SMTP_DATA_ACK:
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-      if (smtp->buffer[0] != '3')
-       {
-         mu_stream_close (mailer->stream);
-         CLEAR_STATE (smtp);
-         return EACCES;
-       }
-      smtp->state = SMTP_SEND;
-
-      if ((smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA) == 0)
-       MU_DEBUG (smtp->mailer->debug, MU_DEBUG_PROT, "> (data...)\n");
-
-    case SMTP_SEND:
-      {
-       mu_stream_t     stream;
-       mu_header_t     hdr;
-       mu_body_t       body;
-
-       /* We may be here after an EAGAIN so check if we have something
-          in the buffer and flush it.  */
-       status = smtp_write (smtp);
-       CHECK_EAGAIN (smtp, status);
-
-       mu_message_get_header (smtp->msg, &hdr);
-       mu_header_get_streamref (hdr, &stream);
-       mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
-       status = send_header (smtp, stream);
-       mu_stream_destroy (&stream);
-       if (status)
-         return status;
-       
-       mu_message_get_body (smtp->msg, &body);
-       mu_body_get_streamref (body, &stream);
-       mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
-       status = send_body (smtp, stream);
-       mu_stream_destroy (&stream);
-      }          
-    case SMTP_SEND_DOT:
-      status = smtp_write (smtp);
-      CHECK_EAGAIN (smtp, status);
-      smtp->state = SMTP_SEND_ACK;
-
-    case SMTP_SEND_ACK:
-      status = smtp_read_ack (smtp);
-      CHECK_EAGAIN (smtp, status);
-      if (smtp->buffer[0] != '2')
-       {
-         mu_stream_close (mailer->stream);
-         CLEAR_STATE (smtp);
-         return EACCES;
-       }
-
-      /* Decide whether we need to loop again, to deliver to Bcc:
-         recipients. */
-      if (!smtp->bccing)
-       {
-         smtp->bccing = 1;
-         smtp->rcpt_index = 1;
-       }
-      if (smtp->rcpt_index <= smtp->rcpt_bcc_count)
-       goto ENV_FROM;
-
-      mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT,
-                           argmsg);
-
-    default:
-      break;
-    }
-  CLEAR_STATE (smtp);
-  return 0;
+  struct _smtp_mailer *smp = mailer->data;
+  return mu_smtp_quit (smp->smtp);
 }
 
+
 int
-smtp_address_add (mu_address_t * paddr, const char *value)
+smtp_address_add (mu_address_t *paddr, const char *value)
 {
-  mu_address_t    addr = NULL;
-  int             status;
+  mu_address_t addr = NULL;
+  int status;
 
   status = mu_address_create (&addr, value);
   if (status)
@@ -1175,20 +250,19 @@ smtp_address_add (mu_address_t * paddr, const char 
*value)
 }
 
 static int
-_smtp_property_is_set (smtp_t smtp, const char *name)
+_smtp_property_is_set (struct _smtp_mailer *smp, const char *name)
 {
-  mu_property_t   property = NULL;
+  mu_property_t property = NULL;
 
-  mu_mailer_get_property (smtp->mailer, &property);
+  mu_mailer_get_property (smp->mailer, &property);
   return mu_property_is_set (property, name);
 }
 
+
 static int
-_smtp_set_rcpt (smtp_t smtp, mu_message_t msg, mu_address_t to)
+_smtp_set_rcpt (struct _smtp_mailer *smp, mu_message_t msg, mu_address_t to)
 {
   int             status = 0;
-  mu_header_t     header = NULL;
-  char           *value;
 
   /* Get RCPT_TO from TO, or the message. */
 
@@ -1197,290 +271,220 @@ _smtp_set_rcpt (smtp_t smtp, mu_message_t msg, 
mu_address_t to)
       /* Use the specified mu_address_t. */
       if ((status = mu_mailer_check_to (to)) != 0)
        {
-         MU_DEBUG (smtp->mailer->debug, MU_DEBUG_ERROR,
+         MU_DEBUG (smp->mailer->debug, MU_DEBUG_ERROR,
                    "mu_mailer_send_message(): explicit to not valid\n");
          return status;
        }
-      smtp->rcpt_to = to;
-      mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count);
+      smp->rcpt_to = to;
 
       if (status)
        return status;
     }
 
-  if (!to || _smtp_property_is_set (smtp, "READ_RECIPIENTS"))
+  if (!to || _smtp_property_is_set (smp, "READ_RECIPIENTS"))
     {
+      mu_header_t     header;
+      
       if ((status = mu_message_get_header (msg, &header)))
        return status;
 
-      status = mu_header_aget_value (header, MU_HEADER_TO, &value);
-
-      if (status == 0)
+      do
        {
-         smtp_address_add (&smtp->rcpt_to, value);
-         free (value);
-       }
-      else if (status != MU_ERR_NOENT)
-       goto end;
-
-      status = mu_header_aget_value (header, MU_HEADER_CC, &value);
+         const char      *value;
+         
+         status = mu_header_sget_value (header, MU_HEADER_TO, &value);
+         if (status == 0)
+           smtp_address_add (&smp->rcpt_to, value);
+         else if (status != MU_ERR_NOENT)
+           break;
 
-      if (status == 0)
-       {
-         smtp_address_add (&smtp->rcpt_to, value);
-         free (value);
-       }
-      else if (status != MU_ERR_NOENT)
-       goto end;
+         status = mu_header_sget_value (header, MU_HEADER_CC, &value);
+         if (status == 0)
+           smtp_address_add (&smp->rcpt_to, value);
+         else if (status != MU_ERR_NOENT)
+           break;
 
-      status = mu_header_aget_value (header, MU_HEADER_BCC, &value);
-      if (status == 0)
-       {
-         smtp_address_add (&smtp->rcpt_bcc, value);
-         free (value);
-       }
-      else if (status != MU_ERR_NOENT)
-       goto end;
+         status = mu_header_sget_value (header, MU_HEADER_BCC, &value);
+         if (status == 0)
+           smtp_address_add (&smp->rcpt_bcc, value);
+         else if (status != MU_ERR_NOENT)
+           break;
 
-      /* If to or bcc is present, the must be OK. */
-      if (smtp->rcpt_to && (status = mu_mailer_check_to (smtp->rcpt_to)))
-       goto end;
+         if (smp->rcpt_to && (status = mu_mailer_check_to (smp->rcpt_to)))
+           break;
 
-      if (smtp->rcpt_bcc && (status = mu_mailer_check_to (smtp->rcpt_bcc)))
-       goto end;
+         if (smp->rcpt_bcc && (status = mu_mailer_check_to (smp->rcpt_bcc)))
+           break;
+       }
+      while (0);
     }
 
-end:
-
   if (status)
     {
-      mu_address_destroy (&smtp->rcpt_to);
-      mu_address_destroy (&smtp->rcpt_bcc);
+      mu_address_destroy (&smp->rcpt_to);
+      mu_address_destroy (&smp->rcpt_bcc);
     }
   else
     {
-      if (smtp->rcpt_to)
-       mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count);
-
-      if (smtp->rcpt_bcc)
-       mu_address_get_count (smtp->rcpt_bcc, &smtp->rcpt_bcc_count);
+      size_t rcpt_cnt, bcc_cnt;
+      
+      if (smp->rcpt_to)
+       mu_address_get_count (smp->rcpt_to, &rcpt_cnt);
+      
+      if (smp->rcpt_bcc)
+       mu_address_get_count (smp->rcpt_bcc, &bcc_cnt);
 
-      if (smtp->rcpt_to_count + smtp->rcpt_bcc_count == 0)
+      if (rcpt_cnt + bcc_cnt == 0)
        status = MU_ERR_MAILER_NO_RCPT_TO;
     }
 
   return status;
 }
 
-/* C99 says that a conforming implementations of snprintf ()
-   should return the number of char that would have been call
-   but many GNU/Linux && BSD implementations return -1 on error.
-   Worse QNX/Neutrino actually does not put the terminal
-   null char.  So let's try to cope.  */
 static int
-smtp_writeline (smtp_t smtp, const char *format, ...)
+_rcpt_to_addr (mu_smtp_t smtp, mu_address_t addr, size_t *pcount)
 {
-  int             len;
-  va_list         ap;
-  int             done = 1;
-
-  va_start (ap, format);
-  do
-    {
-      len = vsnprintf (smtp->buffer, smtp->buflen - 1, format, ap);
-      if (len < 0 || (len >= (int) smtp->buflen)
-         || !memchr (smtp->buffer, '\0', len + 1))
-       {
-         char           *buffer = NULL;
-         size_t          buflen = smtp->buflen * 2;
-
-         buffer = realloc (smtp->buffer, buflen);
-         if (smtp->buffer == NULL)
-           return ENOMEM;
-         smtp->buffer = buffer;
-         smtp->buflen = buflen;
-         done = 0;
-       }
-      else
-       done = 1;
-    }
-  while (!done);
-
-  va_end (ap);
-
-  smtp->ptr = smtp->buffer + len;
+  size_t i, count, rcpt_cnt = 0;
+  int status;
+  
+  status = mu_address_get_count (addr, &count);
+  if (status)
+    return status;
 
-  if ((smtp->state != SMTP_SEND && smtp->state != SMTP_SEND_DOT)
-      || smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA)
+  for (i = 1; i <= count; i++)
     {
-      while (len > 0 && mu_isblank (smtp->buffer[len - 1]))
-       len--;
-      MU_DEBUG2 (smtp->mailer->debug, MU_DEBUG_PROT, "> %.*s\n", len,
-                smtp->buffer);
-    }
-
-  return 0;
-}
+      const char *to = NULL;
 
-static int
-smtp_write (smtp_t smtp)
-{
-  int             status = 0;
-  size_t          len;
-
-  if (smtp->ptr > smtp->buffer)
-    {
-      len = smtp->ptr - smtp->buffer;
-      status = mu_stream_write (smtp->mailer->stream, smtp->buffer, len, NULL);
+      status = mu_address_sget_email (addr, i, &to);
       if (status == 0)
-       memmove (smtp->buffer, smtp->buffer + len, len);
-    }
-  else
-    {
-      smtp->ptr = smtp->buffer;
-      len = 0;
+       {
+         status = mu_smtp_rcpt_basic (smtp, to, NULL);
+         if (status == 0)
+           rcpt_cnt++;
+         else if (status != MU_ERR_REPLY)
+           break;
+       }
     }
+  *pcount = rcpt_cnt;
   return status;
 }
 
 static int
-smtp_read_ack (smtp_t smtp)
+smtp_send_message (mu_mailer_t mailer, mu_message_t msg,
+                  mu_address_t argfrom, mu_address_t argto)
 {
-  int             status;
-  int             multi;
-
-  do
-    {
-      multi = 0;
-      status = smtp_readline (smtp);
-      if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-')
-       multi = 1;
-      if (status == 0)
-       smtp->ptr = smtp->buffer;
-    }
-  while (multi && status == 0);
+  struct _smtp_mailer *smp = mailer->data;
+  mu_smtp_t smtp = smp->smtp;
+  int status;
+  size_t size, lines, count;
+  const char *mail_from;
+  mu_header_t     header;
+      
+  if (mailer == NULL)
+    return EINVAL;
+  
+  smp = mailer->data;
+  if (!smp)
+    return EINVAL;
 
-  if (status == 0)
-    smtp->ptr = smtp->buffer;
-  return status;
-}
+  if ((status = mu_message_get_header (msg, &header)))
+    return status;
+  
+  status = _smtp_set_rcpt (smp, msg, argto);
+  if (status)
+    return status;
 
-static int
-smtp_parse_ehlo_ack (smtp_t smtp)
-{
-  int             status;
-  int             multi;
+  status = mu_address_sget_email (argfrom, 1, &mail_from);
+  if (status)
+    return status;
+  
+  if (mu_smtp_capa_test (smp->smtp, "SIZE", NULL) == 0 &&
+      mu_message_size (msg, &size) == 0 &&
+      mu_message_lines (msg, &lines) == 0)
+    status = mu_smtp_mail_basic (smp->smtp, mail_from,
+                                "SIZE=%lu",
+                                (unsigned long) (size + lines));
+  else
+    status = mu_smtp_mail_basic (smp->smtp, mail_from, NULL);
+  if (status)
+    return status;
 
-  smtp->ptr = smtp->buffer;
+  status = _rcpt_to_addr (smtp, smp->rcpt_to, &count);
+  if (status && count == 0)
+    return status;
+  status = _rcpt_to_addr (smtp, smp->rcpt_bcc, &count);
+  if (status && count == 0)
+    return status;
 
-  do
+  if (mu_header_sget_value (header, MU_HEADER_BCC, NULL))
     {
-      multi = 0;
-      status = smtp_readline (smtp);
-      if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-')
-       multi = 1;
-      if (status == 0 && memcmp (smtp->buffer, "250", 3) == 0)
-       {
-         char *capa_str = smtp->buffer + 4;
-
-         smtp->ptr = smtp->buffer;
+      mu_iterator_t itr;
+      mu_body_t body;
+      mu_stream_t ostr, bstr;
 
-         if (!mu_c_strncasecmp (capa_str, "STARTTLS", 8))
-           smtp->capa |= CAPA_STARTTLS;
-         else if (!mu_c_strncasecmp (capa_str, "SIZE", 4))
-           {
-             char  *p;
-             size_t n;
-             
-             smtp->capa |= CAPA_SIZE;
-
-             n = strtoul (capa_str + 5, &p, 10);
-             if (*p != '\n')
-               MU_DEBUG1 (smtp->mailer->debug, MU_DEBUG_ERROR,
-                          "suspicious size capability: %s",
-                          smtp->buffer);
-             else
-               smtp->max_size = n;
-           }
-         else if (!mu_c_strncasecmp (capa_str, "AUTH", 4))
-           {
-             char           *name, *s;
-
-             smtp->capa |= CAPA_AUTH;
-
-             for (name = strtok_r (capa_str + 5, " ", &s); name;
-                  name = strtok_r (NULL, " ", &s))
-               {
-                 struct auth_mech_record *mechs = auth_mech_list;
-
-                 mu_rtrim_cset (name, "\r\n");
-                 for (; mechs->name; mechs++)
-                   {
-                     if (!mu_c_strcasecmp (mechs->name, name))
-                       {
-                         smtp->auth_mechs |= mechs->id;
-                         break;
-                       }
-                   }
-               }
-           }
+      status = mu_smtp_data (smtp, &ostr);
+      if (status)
+       return status;
+      mu_header_get_iterator (header, &itr);
+      for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+          mu_iterator_next (itr))
+       {
+         const char *name;
+         void *value;
 
+         mu_iterator_current_kv (itr, (void*) &name, &value);
+         if (mu_c_strcasecmp (name, MU_HEADER_BCC) == 0)
+           continue;
+         mu_stream_printf (ostr, "%s: %s\n", name, (char*)value);
        }
+      mu_iterator_destroy (&itr);
+      mu_stream_write (ostr, "\n", 1, NULL);
+      
+      mu_message_get_body (msg, &body);
+      mu_body_get_streamref (body, &bstr);
+      mu_stream_copy (ostr, bstr, 0, NULL);
+      mu_stream_destroy (&bstr);
+      mu_stream_close (ostr);
+      mu_stream_destroy (&ostr);
     }
-  while (multi && status == 0);
-
-  if (status == 0)
-    smtp->ptr = smtp->buffer;
+  else
+    {
+      mu_stream_t str;
+      mu_message_get_streamref (msg, &str);
+      status = mu_smtp_send_stream (smtp, str);
+      mu_stream_destroy (&str);
+    }
+  mu_smtp_quit (smtp);
+  mu_address_destroy (&smp->rcpt_to);
+  mu_address_destroy (&smp->rcpt_bcc);
   return status;
 }
 
-/* Read a complete line form the pop server. Transform CRLF to LF,
-   put a null in the buffer when done.  */
+
 static int
-smtp_readline (smtp_t smtp)
+_mailer_smtp_init (mu_mailer_t mailer)
 {
-  size_t          n = 0;
-  size_t          total = smtp->ptr - smtp->buffer;
-  int             status;
+  struct _smtp_mailer *smp;
 
-  /* Must get a full line before bailing out.  */
-  do
-    {
-      status = mu_stream_readline (smtp->mailer->stream, smtp->buffer + total,
-                                  smtp->buflen - total, &n);
-      if (status != 0)
-       return status;
+  /* Allocate memory specific to smtp mailer.  */
+  smp = mailer->data = calloc (1, sizeof (*smp));
+  if (mailer->data == NULL)
+    return ENOMEM;
 
-      /* Server went away, consider this like an error.  */
-      if (n == 0)
-       return EIO;
+  smp->mailer = mailer;        /* Back pointer.  */
 
-      total += n;
-      smtp->nl = memchr (smtp->buffer, '\n', total);
-      if (smtp->nl == NULL)    /* Do we have a full line.  */
-       {
-         /* Allocate a bigger buffer ?  */
-         if (total >= smtp->buflen - 1)
-           {
-             smtp->buflen *= 2;
-             smtp->buffer = realloc (smtp->buffer, smtp->buflen + 1);
-             if (smtp->buffer == NULL)
-               return ENOMEM;
-           }
-       }
-      smtp->ptr = smtp->buffer + total;
-    }
-  while (smtp->nl == NULL);
+  mailer->_destroy = smtp_destroy;
+  mailer->_open = smtp_open;
+  mailer->_close = smtp_close;
+  mailer->_send_message = smtp_send_message;
 
-  /* \r\n --> \n\0  */
-  if (smtp->nl > smtp->buffer)
-    {
-      *(smtp->nl - 1) = '\n';
-      *(smtp->nl) = '\0';
-      smtp->ptr = smtp->nl;
-    }
+  /* Set our properties.  */
+  {
+    mu_property_t   property = NULL;
 
-  MU_DEBUG1 (smtp->mailer->debug, MU_DEBUG_PROT, "< %s", smtp->buffer);
+    mu_mailer_get_property (mailer, &property);
+    mu_property_set_value (property, "TYPE", "SMTP", 1);
+  }
 
   return 0;
 }
diff --git a/libproto/mailer/smtp_quit.c b/libproto/mailer/smtp_quit.c
index c87544e..104745e 100644
--- a/libproto/mailer/smtp_quit.c
+++ b/libproto/mailer/smtp_quit.c
@@ -37,6 +37,8 @@ mu_smtp_quit (mu_smtp_t smtp)
     return EINVAL;
   if (MU_SMTP_FISSET (smtp, _MU_SMTP_ERR))
     return MU_ERR_FAILURE;
+  if (smtp->state == MU_SMTP_CLOS)
+    return 0;
   status = mu_smtp_write (smtp, "QUIT\r\n");
   MU_SMTP_CHECK_ERROR (smtp, status);
   status = mu_smtp_response (smtp);
diff --git a/mail/send.c b/mail/send.c
index 88c358e..89a3f2d 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -23,7 +23,7 @@
 #include <fcntl.h>
 
 static int isfilename (const char *);
-static void msg_to_pipe (const char *cmd, mu_message_t msg);
+static int msg_to_pipe (const char *cmd, mu_message_t msg);
 
 
 /* Additional message headers */
@@ -338,6 +338,82 @@ fill_body (mu_message_t msg, FILE *file)
   return 0;
 }
 
+static int
+save_dead_message (compose_env_t *env)
+{
+  if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0)
+    {
+      FILE *fp = fopen (getenv ("DEAD"),
+                       mailvar_get (NULL, "appenddeadletter",
+                                    mailvar_type_boolean, 0) == 0 ?
+                       "a" : "w");
+      
+      if (!fp)
+       {
+         util_error (_("Cannot open file %s: %s"), getenv ("DEAD"),
+                     strerror (errno));
+         return 1;
+       }
+      else
+       {
+         char *buf = NULL;
+         size_t n;
+         rewind (env->file);
+         while (getline (&buf, &n, env->file) > 0)
+           fputs (buf, fp);
+         fclose (fp);
+         free (buf);
+       }
+    }
+  return 0;
+}
+
+static int
+send_message (mu_message_t msg)
+{
+  char *sendmail;
+  int status;
+  
+  if (mailvar_get (&sendmail, "sendmail", mailvar_type_string, 0) == 0)
+    {
+      if (sendmail[0] == '/')
+       status = msg_to_pipe (sendmail, msg);
+      else
+       {
+         mu_mailer_t mailer;
+         
+         status = mu_mailer_create (&mailer, sendmail);
+         if (status == 0)
+           {
+             if (mailvar_get (NULL, "verbose", mailvar_type_boolean, 0) == 0)
+               {
+                 mu_debug_t debug = NULL;
+                 mu_mailer_get_debug (mailer, &debug);
+                 mu_debug_set_level (debug,
+                                     MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT));
+               }
+             status = mu_mailer_open (mailer, MU_STREAM_RDWR);
+             if (status == 0)
+               {
+                 status = mu_mailer_send_message (mailer, msg, NULL, NULL);
+                 mu_mailer_close (mailer);
+               }
+             else
+               util_error (_("Cannot open mailer: %s"), mu_strerror (status));
+             mu_mailer_destroy (&mailer);
+           }
+         else
+           util_error (_("Cannot create mailer: %s"),
+                       mu_strerror (status));
+       }
+    }
+  else
+    {
+      util_error (_("Variable sendmail not set: no mailer"));
+      status = ENOSYS;
+    }
+  return status;
+}
 
 /* mail_send0(): shared between mail_send() and mail_reply();
 
@@ -471,41 +547,16 @@ mail_send0 (compose_env_t * env, int save_to)
       free (buf);
     }
 
-  /* If interrupted dump the file to dead.letter.  */
+  /* If interrupted, dump the file to dead.letter.  */
   if (int_cnt)
     {
-      if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0)
-       {
-         FILE *fp = fopen (getenv ("DEAD"),
-                           mailvar_get (NULL, "appenddeadletter",
-                                        mailvar_type_boolean, 0) == 0 ?
-                           "a" : "w");
-
-         if (!fp)
-           {
-             util_error (_("Cannot open file %s: %s"), getenv ("DEAD"),
-                         strerror (errno));
-           }
-         else
-           {
-             char *buf = NULL;
-             size_t n;
-             rewind (env->file);
-             while (getline (&buf, &n, env->file) > 0)
-               fputs (buf, fp);
-             fclose (fp);
-             free (buf);
-           }
-       }
-
+      save_dead_message (env);
       fclose (env->file);
       remove (filename);
       free (filename);
       return 1;
     }
 
-  fclose (env->file);          /* FIXME: freopen would be better */
-
   /* In mailx compatibility mode, ask for Cc and Bcc after editing
      the body of the message */
   if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0) == 0)
@@ -521,12 +572,12 @@ mail_send0 (compose_env_t * env, int save_to)
       file = fopen (filename, "r");
       if (file != NULL)
        {
-         mu_mailer_t mailer;
          mu_message_t msg = NULL;
          int rc;
+         int status = 0;
          
          mu_message_create (&msg, NULL);
-
+         
          /* Fill the body.  */
          rc = fill_body (msg, file);
          fclose (file);
@@ -561,11 +612,10 @@ mail_send0 (compose_env_t * env, int save_to)
                    {
                      /* Pipe to a cmd.  */
                      if (env->outfiles[i][0] == '|')
-                       msg_to_pipe (&(env->outfiles[i][1]), msg);
+                       status = msg_to_pipe (env->outfiles[i] + 1, msg);
                      /* Save to a file.  */
                      else
                        {
-                         int status;
                          mu_mailbox_t mbx = NULL;
                          status = mu_mailbox_create_default (&mbx, 
                                                               
env->outfiles[i]);
@@ -585,64 +635,36 @@ mail_send0 (compose_env_t * env, int save_to)
                            }
                          if (status)
                            util_error (_("Cannot create mailbox %s: %s"), 
-                                       env->outfiles[i], mu_strerror (status));
+                                       env->outfiles[i],
+                                       mu_strerror (status));
                        }
                    }
                }
 
              /* Do we need to Send the message on the wire?  */
-             if (compose_header_get (env, MU_HEADER_TO, NULL)
-                 || compose_header_get (env, MU_HEADER_CC, NULL)
-                 || compose_header_get (env, MU_HEADER_BCC, NULL))
+             if (status == 0 &&
+                 (compose_header_get (env, MU_HEADER_TO, NULL) ||
+                  compose_header_get (env, MU_HEADER_CC, NULL) ||
+                  compose_header_get (env, MU_HEADER_BCC, NULL)))
                {
-                 char *sendmail;
-                 if (mailvar_get (&sendmail, "sendmail",
-                                  mailvar_type_string, 0) == 0)
-                   {
-                     mu_message_set_header (msg, env->header, NULL);
-                     env->header = NULL;
-                     if (sendmail[0] == '/')
-                       msg_to_pipe (sendmail, msg);
-                     else
-                       {
-                         int status = mu_mailer_create (&mailer, sendmail);
-                         if (status == 0)
-                           {
-                             if (mailvar_get (NULL, "verbose",
-                                              mailvar_type_boolean, 0) == 0)
-                               {
-                                 mu_debug_t debug = NULL;
-                                 mu_mailer_get_debug (mailer, &debug);
-                                 mu_debug_set_level (debug,
-                                                     MU_DEBUG_LEVEL_UPTO 
(MU_DEBUG_PROT));
-                               }
-                             status = mu_mailer_open (mailer, MU_STREAM_RDWR);
-                             if (status == 0)
-                               {
-                                 mu_mailer_send_message (mailer, msg,
-                                                         NULL, NULL);
-                                 mu_mailer_close (mailer);
-                               }
-                             else
-                               util_error (_("Cannot open mailer: %s"),
-                                           mu_strerror (status));
-                             mu_mailer_destroy (&mailer);
-                           }
-                         else
-                           util_error (_("Cannot create mailer: %s"),
-                                       mu_strerror (status));
-                       }
-                   }
-                 else
-                   util_error (_("Variable sendmail not set: no mailer"));
+                 mu_message_set_header (msg, env->header, NULL);
+                 env->header = NULL;
+                 status = send_message (msg);
+                 if (status)
+                   save_dead_message (env);
                }
            }
+         fclose (env->file);
          mu_message_destroy (&msg, NULL);
          remove (filename);
          free (filename);
-         return 0;
+         return status;
        }
     }
+  else
+    save_dead_message (env);
+  
+  fclose (env->file);
 
   remove (filename);
   free (filename);
@@ -662,26 +684,33 @@ isfilename (const char *p)
 
 /* FIXME: Should probably be in util.c.  */
 /* Call popen(cmd) and write the message to it.  */
-static void
+static int
 msg_to_pipe (const char *cmd, mu_message_t msg)
 {
-  FILE *fp = popen (cmd, "w");
-  if (fp)
+  mu_stream_t progstream, msgstream;
+  int status, rc;
+  
+  status = mu_prog_stream_create (&progstream, cmd, MU_STREAM_WRITE);
+  if (status)
     {
-      mu_stream_t stream = NULL;
-      char buffer[512];
-      size_t n = 0;
-      /* FIXME: Use mu_stream_copy */
-      mu_message_get_streamref (msg, &stream);
-      while (mu_stream_read (stream, buffer, sizeof buffer - 1, &n) == 0
-            && n != 0)
-       {
-         buffer[n] = '\0';
-         fprintf (fp, "%s", buffer);
-       }
-      mu_stream_destroy (&stream);
-      pclose (fp);
+      util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status));
+      return status;
     }
-  else
-    util_error (_("Piping %s failed"), cmd);
+
+  mu_message_get_streamref (msg, &msgstream);
+  status = mu_stream_copy (progstream, msgstream, 0, NULL);
+  rc = mu_stream_close (progstream);
+
+  if (status == 0 && rc)
+    status = rc;
+
+  mu_stream_destroy (&progstream);
+  mu_stream_destroy (&msgstream);
+  
+  if (status)
+    {
+      util_error (_("Sending data to %s failed: %s"), cmd,
+                 mu_strerror (status));
+    }
+  return status;
 }


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]