commit-mailutils
[Top][All Lists]
Advanced

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

[SCM] GNU Mailutils branch, stream-cleanup, updated. rel-2_1-117-g607f57


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, stream-cleanup, updated. rel-2_1-117-g607f573
Date: Fri, 03 Sep 2010 10:01:05 +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=607f57384987efc9f08a1febff4c6ab00e61e2ab

The branch, stream-cleanup has been updated
       via  607f57384987efc9f08a1febff4c6ab00e61e2ab (commit)
       via  64c8eabf83ec6f1a7b1050a1929b4387f78b7ccc (commit)
      from  96fb7b6c2142e6da7ea4c56923c6289b1bfd244e (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 607f57384987efc9f08a1febff4c6ab00e61e2ab
Author: Sergey Poznyakoff <address@hidden>
Date:   Fri Sep 3 12:49:33 2010 +0300

    Re-implement GSASL support.
    
    * libmu_auth/lbuf.c: Removed.
    * libmu_auth/lbuf.h: Removed.
    * libmu_auth/Makefile.am : Remove lbuf stuff.
    
    * include/mailutils/sys/gsasl-stream.h: New file.
    * include/mailutils/sys/Makefile.am: Add gsasl-stream.h.
    * include/mailutils/gsasl.h (mu_gsasl_stream_create): Remove.
    (gsasl_encoder_stream, gsasl_decoder_stream): New prototypes.
    
    * libmu_auth/gsasl.c: Rewrite.
    
    * imap4d/authenticate.c (auth_data): Remove.
    Use struct imap4d_auth instead.
    (_auth_try): Use new authentication API.
    (imap4d_authenticate): Likewise.
    * imap4d/imap4d.h (util_register_event, util_event_remove)
    (util_run_events): Remove.
    (imap4d_auth_handler_fp): Change prototype.
    (imap4d_auth): New struct.
    (imap4d_auth_result): New enum.
    * imap4d/io.c (io_format_completion_response)
    (io_stream_completion_response): New functions.
    (io_completion_response): Rewrite using io_format_completion_response.
    * imap4d/util.c (sc2string): Remove leftover prototype.
    (util_register_event, util_event_remove)
    (util_run_events): Remove.
    * imap4d/auth_gsasl.c: Revamp using new streams and the new
    authentication interface.
    * imap4d/auth_gss.c: Likewise (though yet untested).
    
    * mailbox/xscript-stream.c (_xscript_ctl): Remove unused variables.

commit 64c8eabf83ec6f1a7b1050a1929b4387f78b7ccc
Author: Sergey Poznyakoff <address@hidden>
Date:   Fri Sep 3 09:39:13 2010 +0300

    Port the new I/O scheme from pop3d to imap4d.
    
    * mailbox/fltstream.c (filter_wr_flush): Fix erroneous conditional.
    (filter_wait): New method.
    (mu_filter_stream_create): Set wait method.
    
    * imap4d/io.c: New source.
    * imap4d/Makefile.am: Add io.c
    * imap4d/*: Update I/O function calls.

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

Summary of changes:
 imap4d/Makefile.am                                 |    1 +
 imap4d/append.c                                    |   15 +-
 imap4d/auth_gsasl.c                                |  149 +++--
 imap4d/auth_gss.c                                  |   74 ++-
 imap4d/authenticate.c                              |   72 +-
 imap4d/bye.c                                       |   17 +-
 imap4d/capability.c                                |   10 +-
 imap4d/check.c                                     |    4 +-
 imap4d/close.c                                     |    6 +-
 imap4d/copy.c                                      |    6 +-
 imap4d/create.c                                    |   10 +-
 imap4d/delete.c                                    |   10 +-
 imap4d/examine.c                                   |    2 +-
 imap4d/expunge.c                                   |    4 +-
 imap4d/fetch.c                                     |  232 ++++----
 imap4d/id.c                                        |   12 +-
 imap4d/idle.c                                      |   19 +-
 imap4d/imap4d.c                                    |   11 +-
 imap4d/imap4d.h                                    |   70 ++-
 imap4d/io.c                                        |  616 +++++++++++++++++
 imap4d/list.c                                      |   35 +-
 imap4d/login.c                                     |   15 +-
 imap4d/logout.c                                    |    2 +-
 imap4d/lsub.c                                      |   15 +-
 imap4d/namespace.c                                 |   22 +-
 imap4d/noop.c                                      |    4 +-
 imap4d/rename.c                                    |   19 +-
 imap4d/search.c                                    |   10 +-
 imap4d/select.c                                    |   30 +-
 imap4d/starttls.c                                  |   10 +-
 imap4d/status.c                                    |   32 +-
 imap4d/store.c                                     |   10 +-
 imap4d/subscribe.c                                 |    6 +-
 imap4d/sync.c                                      |   16 +-
 imap4d/uid.c                                       |    4 +-
 imap4d/unsubscribe.c                               |    6 +-
 imap4d/util.c                                      |  729 +-------------------
 include/mailutils/gsasl.h                          |    6 +-
 include/mailutils/sys/Makefile.am                  |    1 +
 .../sys/{xscript-stream.h => gsasl-stream.h}       |   17 +-
 libmu_auth/Makefile.am                             |    2 -
 libmu_auth/gsasl.c                                 |  316 ++++-----
 libmu_auth/lbuf.c                                  |  167 -----
 libmu_auth/lbuf.h                                  |   37 -
 mailbox/fltstream.c                                |   11 +-
 mailbox/xscript-stream.c                           |    1 -
 46 files changed, 1293 insertions(+), 1570 deletions(-)
 create mode 100644 imap4d/io.c
 copy include/mailutils/sys/{xscript-stream.h => gsasl-stream.h} (78%)
 delete mode 100644 libmu_auth/lbuf.c
 delete mode 100644 libmu_auth/lbuf.h

diff --git a/imap4d/Makefile.am b/imap4d/Makefile.am
index 65a5b8c..bb0c19b 100644
--- a/imap4d/Makefile.am
+++ b/imap4d/Makefile.am
@@ -41,6 +41,7 @@ imap4d_SOURCES = \
  idle.c\
  imap4d.c\
  imap4d.h\
+ io.c\
  list.c\
  logout.c\
  login.c\
diff --git a/imap4d/append.c b/imap4d/append.c
index e549b59..3a5c6cc 100644
--- a/imap4d/append.c
+++ b/imap4d/append.c
@@ -140,11 +140,11 @@ imap4d_append (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   char *err_text = "[TRYCREATE] failed";
   
   if (argc < 4)
-    return util_finish (command, RESP_BAD, "Too few arguments");
+    return io_completion_response (command, RESP_BAD, "Too few arguments");
       
   mboxname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   if (!mboxname)
-    return util_finish (command, RESP_BAD, "Too few arguments");
+    return io_completion_response (command, RESP_BAD, "Too few arguments");
 
   i = IMAP4_ARG_2;
   if (imap4d_tokbuf_getarg (tok, i)[0] == '(')
@@ -160,7 +160,8 @@ imap4d_append (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
            break;
        }
       if (i == argc)
-       return util_finish (command, RESP_BAD, "Missing closing parenthesis");
+       return io_completion_response (command, RESP_BAD, 
+                                      "Missing closing parenthesis");
       i++;
     }
 
@@ -177,14 +178,14 @@ imap4d_append (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       break;
 
     default:
-      return util_finish (command, RESP_BAD, "Too many arguments");
+      return io_completion_response (command, RESP_BAD, "Too many arguments");
     }
 
   msg_text = imap4d_tokbuf_getarg (tok, i);
   
   mboxname = namespace_getfullpath (mboxname, "/", NULL);
   if (!mboxname)
-    return util_finish (command, RESP_NO, "Couldn't open mailbox"); 
+    return io_completion_response (command, RESP_NO, "Couldn't open mailbox"); 
 
   status = mu_mailbox_create_default (&dest_mbox, mboxname);
   if (status == 0)
@@ -202,9 +203,9 @@ imap4d_append (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   
   free (mboxname);
   if (status == 0)
-    return util_finish (command, RESP_OK, "Completed");
+    return io_completion_response (command, RESP_OK, "Completed");
 
-  return util_finish (command, RESP_NO, err_text);
+  return io_completion_response (command, RESP_NO, err_text);
 }
 
 
diff --git a/imap4d/auth_gsasl.c b/imap4d/auth_gsasl.c
index 15e55ce..b16cd38 100644
--- a/imap4d/auth_gsasl.c
+++ b/imap4d/auth_gsasl.c
@@ -29,43 +29,6 @@ static Gsasl_session *sess_ctx;
 
 static void auth_gsasl_capa_init (int disable);
 
-static int
-create_gsasl_stream (mu_stream_t *newstr, mu_stream_t transport, int flags)
-{
-  int rc;
-  
-  rc = mu_gsasl_stream_create (newstr, transport, sess_ctx, flags);
-  if (rc)
-    {
-      mu_diag_output (MU_DIAG_ERROR, _("cannot create SASL stream: %s"),
-             mu_strerror (rc));
-      return RESP_NO;
-    }
-
-  if ((rc = mu_stream_open (*newstr)) != 0)
-    {
-      mu_diag_output (MU_DIAG_ERROR,
-                     _("cannot open SASL input stream: %s"),
-                     mu_stream_strerror (*newstr, rc));
-      return RESP_NO;
-    }
-
-  return RESP_OK;
-}
-
-int
-gsasl_replace_streams (void *self, void *data)
-{
-  mu_stream_t *s = data;
-
-  util_set_input (s[0]);
-  util_set_output (s[1]);
-  free (s);
-  util_event_remove (self);
-  free (self);
-  return 0;
-}
-
 static void
 finish_session (void)
 {
@@ -73,7 +36,21 @@ finish_session (void)
 }
 
 static int
-auth_gsasl (struct imap4d_command *command, char *auth_type, char **username)
+restore_and_return (struct imap4d_auth *ap, mu_stream_t *str, int resp)
+{
+  int rc = mu_stream_ioctl (iostream, MU_IOCTL_SWAP_STREAM, str);
+  if (rc)
+    {
+      mu_error (_("%s failed when it should not: %s"), "MU_IOCTL_SWAP_STREAM",
+               mu_stream_strerror (iostream, rc));
+      abort ();
+    }
+  ap->response = resp;
+  return imap4d_auth_resp;
+}
+
+static enum imap4d_auth_result
+auth_gsasl (struct imap4d_auth *ap)
 {
   char *input_str = NULL;
   size_t input_size = 0;
@@ -81,22 +58,22 @@ auth_gsasl (struct imap4d_command *command, char 
*auth_type, char **username)
   char *output;
   int rc;
   
-  rc = gsasl_server_start (ctx, auth_type, &sess_ctx);
+  rc = gsasl_server_start (ctx, ap->auth_type, &sess_ctx);
   if (rc != GSASL_OK)
     {
       mu_diag_output (MU_DIAG_NOTICE, _("SASL gsasl_server_start: %s"),
                      gsasl_strerror (rc));
-      return 0;
+      return imap4d_auth_fail;
     }
 
-  gsasl_callback_hook_set (ctx, username);
+  gsasl_callback_hook_set (ctx, &ap->username);
 
   output = NULL;
   while ((rc = gsasl_step64 (sess_ctx, input_str, &output))
           == GSASL_NEEDS_MORE)
     {
-      util_send ("+ %s\n", output);
-      imap4d_getline (&input_str, &input_size, &input_len);
+      io_sendf ("+ %s\n", output);
+      io_getline (&input_str, &input_size, &input_len);
     }
   
   if (rc != GSASL_OK)
@@ -105,58 +82,96 @@ auth_gsasl (struct imap4d_command *command, char 
*auth_type, char **username)
                      gsasl_strerror (rc));
       free (input_str);
       free (output);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
   /* Some SASL mechanisms output additional data when GSASL_OK is
      returned, and clients must respond with an empty response. */
   if (output[0])
     {
-      util_send ("+ %s\n", output);
-      imap4d_getline (&input_str, &input_size, &input_len);
+      io_sendf ("+ %s\n", output);
+      io_getline (&input_str, &input_size, &input_len);
       if (input_len != 0)
        {
          mu_diag_output (MU_DIAG_NOTICE, _("non-empty client response"));
           free (input_str);
           free (output);
-         return RESP_NO;
+         ap->response = RESP_NO;
+         return imap4d_auth_resp;
        }
     }
 
   free (input_str);
   free (output);
 
-  if (*username == NULL)
+  if (ap->username == NULL)
     {
-      mu_diag_output (MU_DIAG_NOTICE, _("GSASL %s: cannot get username"), 
auth_type);
-      return RESP_NO;
+      mu_diag_output (MU_DIAG_NOTICE, _("GSASL %s: cannot get username"),
+                     ap->auth_type);
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
+  auth_gsasl_capa_init (1);
   if (sess_ctx)
     {
-      mu_stream_t tmp, new_in, new_out;
-      mu_stream_t *s;
-
-      util_get_input (&tmp);
-      if (create_gsasl_stream (&new_in, tmp, MU_STREAM_READ))
-       return RESP_NO;
-      util_get_output (&tmp);
-      if (create_gsasl_stream (&new_out, tmp, MU_STREAM_WRITE))
+      mu_stream_t stream[2], newstream[2];
+
+      stream[0] = stream[1] = NULL;
+      rc = mu_stream_ioctl (iostream, MU_IOCTL_SWAP_STREAM, stream);
+      if (rc)
+       {
+         mu_error (_("%s failed: %s"), "MU_IOCTL_SWAP_STREAM",
+                   mu_stream_strerror (iostream, rc));
+         ap->response = RESP_NO;
+         return imap4d_auth_resp;
+       }
+      rc = gsasl_encoder_stream (&newstream[0], stream[0], sess_ctx,
+                                MU_STREAM_READ);
+      if (rc)
+       {
+         mu_error (_("%s failed: %s"), "gsasl_encoder_stream",
+                   mu_strerror (rc));
+         return restore_and_return (ap, stream, RESP_NO);
+       }
+
+      rc = gsasl_decoder_stream (&newstream[1], stream[1], sess_ctx,
+                                MU_STREAM_WRITE);
+      if (rc)
+       {
+         mu_error (_("%s failed: %s"), "gsasl_decoder_stream",
+                   mu_strerror (rc));
+         mu_stream_destroy (&newstream[0]);
+         return restore_and_return (ap, stream, RESP_NO);
+       }
+      
+      if (ap->username)
        {
-         mu_stream_destroy (&new_in);
-         return RESP_NO;
+         if (imap4d_session_setup (ap->username))
+           return restore_and_return (ap, stream, RESP_NO);
        }
 
-      s = calloc (2, sizeof (mu_stream_t));
-      s[0] = new_in;
-      s[1] = new_out;
-      util_register_event (STATE_NONAUTH, STATE_AUTH,
-                          gsasl_replace_streams, s);
+      /* FIXME: This is not reflected in the transcript. */
+      io_stream_completion_response (stream[1], ap->command, RESP_OK,
+                                    "%s authentication successful",
+                                    ap->auth_type);
+      mu_stream_flush (stream[1]);
+      
+      rc = mu_stream_ioctl (iostream, MU_IOCTL_SWAP_STREAM, newstream);
+      if (rc)
+       {
+         mu_error (_("%s failed when it should not: %s"),
+                   "MU_IOCTL_SWAP_STREAM",
+                   mu_stream_strerror (iostream, rc));
+         abort ();
+       }
       util_atexit (finish_session);
+      return imap4d_auth_ok;
     }
   
-  auth_gsasl_capa_init (1);
-  return RESP_OK;
+  ap->response = RESP_OK;
+  return imap4d_auth_resp;
 }
 
 static void
diff --git a/imap4d/auth_gss.c b/imap4d/auth_gss.c
index 1a7fd95..28ce74a 100644
--- a/imap4d/auth_gss.c
+++ b/imap4d/auth_gss.c
@@ -108,9 +108,8 @@ imap4d_gss_userok (gss_buffer_t client_name, char *name)
 }
 #endif
 
-static int
-auth_gssapi (struct imap4d_command *command,
-            char *auth_type_unused, char **username)
+static enum imap4d_auth_result
+auth_gssapi (struct imap4d_auth *ap)
 {
   gss_buffer_desc tokbuf, outbuf;
   OM_uint32 maj_stat, min_stat, min_stat2;
@@ -147,7 +146,8 @@ auth_gssapi (struct imap4d_command *command,
   if (maj_stat != GSS_S_COMPLETE)
     {
       display_status ("import name", maj_stat, min_stat);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
   maj_stat = gss_acquire_cred (&min_stat, server_name, 0,
@@ -158,13 +158,14 @@ auth_gssapi (struct imap4d_command *command,
   if (maj_stat != GSS_S_COMPLETE)
     {
       display_status ("acquire credentials", maj_stat, min_stat);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
   /* Start the dialogue */
 
-  util_send ("+ \n");
-  util_flush_output ();
+  io_sendf ("+ \n");
+  io_flush ();
   
   context = GSS_C_NO_CONTEXT;
 
@@ -172,7 +173,7 @@ auth_gssapi (struct imap4d_command *command,
     {
       OM_uint32 ret_flags;
       
-      imap4d_getline (&token_str, &token_size, &token_len);
+      io_getline (&token_str, &token_size, &token_len);
       mu_base64_decode ((unsigned char*) token_str, token_len, &tmp, &size);
       tokbuf.value = tmp;
       tokbuf.length = size;
@@ -192,7 +193,7 @@ auth_gssapi (struct imap4d_command *command,
          if (outbuf.length)
            {
              mu_base64_encode (outbuf.value, outbuf.length, &tmp, &size);
-             util_send ("+ %s\n", tmp);
+             io_sendf ("+ %s\n", tmp);
              free (tmp);
              gss_release_buffer (&min_stat, &outbuf);
            }
@@ -206,16 +207,17 @@ auth_gssapi (struct imap4d_command *command,
       maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
       gss_release_buffer (&min_stat, &outbuf);
       free (token_str);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
   if (outbuf.length)
     {
       mu_base64_encode (outbuf.value, outbuf.length, &tmp, &size);
-      util_send ("+ %s\n", tmp);
+      io_sendf ("+ %s\n", tmp);
       free (tmp);
       gss_release_buffer (&min_stat, &outbuf);
-      imap4d_getline (&token_str, &token_size, &token_len);
+      io_getline (&token_str, &token_size, &token_len);
     }
 
   /* Construct security-level data */
@@ -228,14 +230,15 @@ auth_gssapi (struct imap4d_command *command,
     {
       display_status ("wrap", maj_stat, min_stat);
       free (token_str);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
   
   mu_base64_encode (outbuf.value, outbuf.length, &tmp, &size);
-  util_send ("+ %s\n", tmp);
+  io_sendf ("+ %s\n", tmp);
   free (tmp);
 
-  imap4d_getline (&token_str, &token_size, &token_len);
+  io_getline (&token_str, &token_size, &token_len);
   mu_base64_decode ((unsigned char *) token_str, token_len,
                    (unsigned char **) &tokbuf.value, &tokbuf.length);
   free (token_str);
@@ -246,7 +249,8 @@ auth_gssapi (struct imap4d_command *command,
   if (maj_stat != GSS_S_COMPLETE)
     {
       display_status ("unwrap", maj_stat, min_stat);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
   
   sec_level = ntohl (*(OM_uint32 *) outbuf.value);
@@ -261,23 +265,25 @@ auth_gssapi (struct imap4d_command *command,
       gss_release_buffer (&min_stat, &outbuf);
       maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
       gss_release_buffer (&min_stat, &outbuf);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
   protection_mech = mech;
   client_buffer_size = sec_level & 0x00ffffffff;
 
-  *username = malloc (outbuf.length - 4 + 1);
-  if (!*username)
+  ap->username = malloc (outbuf.length - 4 + 1);
+  if (!ap->username)
     {
       mu_diag_output (MU_DIAG_NOTICE, _("not enough memory"));
       gss_release_buffer (&min_stat, &outbuf);
       maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
       gss_release_buffer (&min_stat, &outbuf);
-      return RESP_NO;
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
        
-  memcpy (*username, (char *) outbuf.value + 4, outbuf.length - 4);
-  (*username)[outbuf.length - 4] = '\0';
+  memcpy (ap->username, (char *) outbuf.value + 4, outbuf.length - 4);
+  ap->username[outbuf.length - 4] = '\0';
   gss_release_buffer (&min_stat, &outbuf);
 
   maj_stat = gss_display_name (&min_stat, client, &client_name, &mech_type);
@@ -286,36 +292,40 @@ auth_gssapi (struct imap4d_command *command,
       display_status ("get client name", maj_stat, min_stat);
       maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
       gss_release_buffer (&min_stat, &outbuf);
-      free (*username);
-      return RESP_NO;
+      free (ap->username);
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
 
 #ifdef WITH_GSS
-  baduser = !gss_userok (client, *username);
+  baduser = !gss_userok (client, ap->username);
 #else
-  baduser = imap4d_gss_userok (&client_name, *username);
+  baduser = imap4d_gss_userok (&client_name, ap->username);
 #endif
 
   if (baduser)
     {
-      mu_diag_output (MU_DIAG_NOTICE, _("GSSAPI user %s is NOT authorized as 
%s"),
-             (char *) client_name.value, *username);
+      mu_diag_output (MU_DIAG_NOTICE,
+                     _("GSSAPI user %s is NOT authorized as %s"),
+                     (char *) client_name.value, ap->username);
       maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
       gss_release_buffer (&min_stat, &outbuf);
       gss_release_buffer (&min_stat, &client_name);
-      free (*username);
-      return RESP_NO;
+      free (ap->username);
+      ap->response = RESP_NO;
+      return imap4d_auth_resp;
     }
   else
     {
       mu_diag_output (MU_DIAG_NOTICE, _("GSSAPI user %s is authorized as %s"),
-             (char *) client_name.value, *username);
+             (char *) client_name.value, ap->username);
     }
 
   gss_release_buffer (&min_stat, &client_name);
   maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
   gss_release_buffer (&min_stat, &outbuf);
-  return RESP_OK;
+  ap->response = RESP_OK;
+  return imap4d_auth_resp;
 }
 
 void
diff --git a/imap4d/authenticate.c b/imap4d/authenticate.c
index 1354203..166294c 100644
--- a/imap4d/authenticate.c
+++ b/imap4d/authenticate.c
@@ -19,7 +19,8 @@
 
 #include "imap4d.h"
 
-struct imap_auth {
+struct imap_auth
+{
   char *name;
   imap4d_auth_handler_fp handler;
 };
@@ -62,28 +63,21 @@ static int
 _auth_capa (void *item, void *usused)
 {
   struct imap_auth *p = item;
-  util_send(" AUTH=%s", p->name);
+  io_sendf (" AUTH=%s", p->name);
   return 0;
 }
 
-struct auth_data {
-  struct imap4d_command *command;
-  char *auth_type;
-  char *arg;
-  char *username;
-  int result;
-};
-
 static int
 _auth_try (void *item, void *data)
 {
   struct imap_auth *p = item;
-  struct auth_data *ap = data;
+  struct imap4d_auth *ap = data;
 
   if (strcmp (p->name, ap->auth_type) == 0)
     {
-      ap->result = p->handler (ap->command, ap->auth_type, &ap->username);
-      return 1;
+      int res = p->handler (ap);
+      if (res)
+       return res;
     }
   return 0;
 }
@@ -104,37 +98,49 @@ int
 imap4d_authenticate (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   char *auth_type;
-  struct auth_data adata;
-
+  struct imap4d_auth adata;
+  enum imap4d_auth_result res;
+  
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   auth_type = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
 
   if (tls_required)
-    return util_finish (command, RESP_NO,
-                       "Command disabled: Use STARTTLS first");
+    return io_completion_response (command, RESP_NO,
+                                  "Command disabled: Use STARTTLS first");
   
   adata.command = command;
   adata.auth_type = auth_type;
-  adata.arg = NULL;
   adata.username = NULL;
 
-  if (mu_list_do (imap_auth_list, _auth_try, &adata) == 0)
-    return util_finish (command, RESP_NO,
-                       "Authentication mechanism not supported");
-  
-  if (adata.result == RESP_OK && adata.username)
+  res = mu_list_do (imap_auth_list, _auth_try, &adata);
+
+  switch (res)
     {
-      if (imap4d_session_setup (adata.username))
-       return util_finish (command, RESP_NO,
-                           "User name or passwd rejected");
-      else
-       return util_finish (command, RESP_OK,
-                           "%s authentication successful", auth_type);
+    case imap4d_auth_nosup:
+      return io_completion_response (command, RESP_NO,
+                                    "Authentication mechanism not supported");
+    case imap4d_auth_ok:
+      return 0;
+
+    case imap4d_auth_resp:
+      if (adata.response == RESP_OK && adata.username)
+       {
+         if (imap4d_session_setup (adata.username))
+           return io_completion_response (command, RESP_NO,
+                                          "User name or passwd rejected");
+         else
+           return io_completion_response (command, RESP_OK,
+                                          "%s authentication successful",
+                                          auth_type);
+       }
+      /* fall through */
+    case imap4d_auth_fail:
+      adata.response = RESP_NO;
+      break;
     }
-      
-  return util_finish (command, adata.result,
-                     "%s authentication failed", auth_type);
+  return io_completion_response (command, adata.response,
+                                "%s authentication failed", auth_type);
 }
 
diff --git a/imap4d/bye.c b/imap4d/bye.c
index 832fc7c..16b67df 100644
--- a/imap4d/bye.c
+++ b/imap4d/bye.c
@@ -40,13 +40,13 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
   switch (reason)
     {
     case ERR_NO_MEM:
-      util_out (RESP_BYE, "Server terminating: no more resources.");
+      io_untagged_response (RESP_BYE, "Server terminating: no more 
resources.");
       mu_diag_output (MU_DIAG_ERROR, _("not enough memory"));
       break;
 
     case ERR_TERMINATE:
       status = EX_OK;
-      util_out (RESP_BYE, "Server terminating on request.");
+      io_untagged_response (RESP_BYE, "Server terminating on request.");
       mu_diag_output (MU_DIAG_NOTICE, _("terminating on request"));
       break;
 
@@ -56,7 +56,7 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
 
     case ERR_TIMEOUT:
       status = EX_TEMPFAIL;
-      util_out (RESP_BYE, "Session timed out");
+      io_untagged_response (RESP_BYE, "Session timed out");
       if (state == STATE_NONAUTH)
         mu_diag_output (MU_DIAG_INFO, _("session timed out for no user"));
       else
@@ -77,10 +77,15 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
       status = EX_OSERR;
       mu_diag_output (MU_DIAG_ERROR, _("mailbox modified by third party"));
       break;
+
+    case ERR_STREAM_CREATE:
+      status = EX_UNAVAILABLE;
+      mu_diag_output (MU_DIAG_ERROR, _("cannot create transport stream"));
+      break;
       
     case OK:
       status = EX_OK;
-      util_out (RESP_BYE, "Session terminating.");
+      io_untagged_response (RESP_BYE, "Session terminating.");
       if (state == STATE_NONAUTH)
        mu_diag_output (MU_DIAG_INFO, _("session terminating"));
       else
@@ -88,13 +93,13 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
       break;
 
     default:
-      util_out (RESP_BYE, "Quitting (reason unknown)");
+      io_untagged_response (RESP_BYE, "Quitting (reason unknown)");
       mu_diag_output (MU_DIAG_ERROR, _("quitting (numeric reason %d)"), 
reason);
       break;
     }
 
   if (status == EX_OK && command)
-     util_finish (command, RESP_OK, "Completed");
+     io_completion_response (command, RESP_OK, "Completed");
 
   util_bye ();
 
diff --git a/imap4d/capability.c b/imap4d/capability.c
index b52d926..02b5664 100644
--- a/imap4d/capability.c
+++ b/imap4d/capability.c
@@ -65,7 +65,7 @@ imap4d_capability_init ()
 static int
 print_capa (void *item, void *data)
 {
-  util_send (" %s", (char *)item);
+  io_sendf (" %s", (char *)item);
   return 0;
 }
 
@@ -73,14 +73,14 @@ int
 imap4d_capability (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
-  util_send ("* CAPABILITY");
+  io_sendf ("* CAPABILITY");
 
   mu_list_do (capa_list, print_capa, NULL);
   
   imap4d_auth_capability ();
-  util_send ("\n");
+  io_sendf ("\n");
 
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/check.c b/imap4d/check.c
index ee1897d..4713a94 100644
--- a/imap4d/check.c
+++ b/imap4d/check.c
@@ -34,6 +34,6 @@ int
 imap4d_check (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
-  return util_finish (command, RESP_OK, "Completed");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/close.c b/imap4d/close.c
index e730268..49ede1c 100644
--- a/imap4d/close.c
+++ b/imap4d/close.c
@@ -27,7 +27,7 @@ imap4d_close0 (struct imap4d_command *command, 
imap4d_tokbuf_t tok,
   int status, flags;
 
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   mu_mailbox_get_flags (mbox, &flags);
   if (flags & MU_STREAM_WRITE)
@@ -51,8 +51,8 @@ imap4d_close0 (struct imap4d_command *command, 
imap4d_tokbuf_t tok,
   mu_mailbox_destroy (&mbox);
 
   if (msg)
-    return util_finish (command, RESP_NO, msg);
-  return util_finish (command, RESP_OK, "Completed");
+    return io_completion_response (command, RESP_NO, msg);
+  return io_completion_response (command, RESP_OK, "Completed");
 }
 
 /*
diff --git a/imap4d/copy.c b/imap4d/copy.c
index 7f9acad..ef7aea0 100644
--- a/imap4d/copy.c
+++ b/imap4d/copy.c
@@ -42,7 +42,7 @@ imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
   char *text;
 
   if (imap4d_tokbuf_argc (tok) != 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   rc = imap4d_copy0 (tok, 0, &text);
   
@@ -52,9 +52,9 @@ imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
       int new_state = (rc == RESP_OK) ? command->success : command->failure;
       if (new_state != STATE_NONE)
        state = new_state;
-      return util_send ("%s %s\n", command->tag, text);
+      return io_sendf ("%s %s\n", command->tag, text);
     }
-  return util_finish (command, rc, "%s", text);
+  return io_completion_response (command, rc, "%s", text);
 }
 
 int
diff --git a/imap4d/create.c b/imap4d/create.c
index 0768d9f..918926a 100644
--- a/imap4d/create.c
+++ b/imap4d/create.c
@@ -91,16 +91,16 @@ imap4d_create (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   const char *msg = "Completed";
 
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
 
   if (*name == '\0')
-    return util_finish (command, RESP_BAD, "Too few arguments");
+    return io_completion_response (command, RESP_BAD, "Too few arguments");
 
   /* Creating, "Inbox" should always fail.  */
   if (mu_c_strcasecmp (name, "INBOX") == 0)
-    return util_finish (command, RESP_BAD, "Already exist");
+    return io_completion_response (command, RESP_BAD, "Already exist");
 
   /* RFC 3501:
          If the mailbox name is suffixed with the server's hierarchy
@@ -117,7 +117,7 @@ imap4d_create (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   name = namespace_getfullpath (name, delim, &ns);
 
   if (!name)
-    return util_finish (command, RESP_NO, "Cannot create mailbox");
+    return io_completion_response (command, RESP_NO, "Cannot create mailbox");
 
   /* It will fail if the mailbox already exists.  */
   if (access (name, F_OK) != 0)
@@ -165,5 +165,5 @@ imap4d_create (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       msg = "already exists";
     }
 
-  return util_finish (command, rc, msg);
+  return io_completion_response (command, rc, msg);
 }
diff --git a/imap4d/delete.c b/imap4d/delete.c
index b6f4f90..b7d30ae 100644
--- a/imap4d/delete.c
+++ b/imap4d/delete.c
@@ -39,25 +39,25 @@ imap4d_delete (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   char *name;
 
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   if (!name || *name == '\0')
-    return util_finish (command, RESP_BAD, "Too few arguments");
+    return io_completion_response (command, RESP_BAD, "Too few arguments");
 
   /* It is an error to attempt to delele "INBOX or a mailbox
      name that dos not exists.  */
   if (mu_c_strcasecmp (name, "INBOX") == 0)
-    return util_finish (command, RESP_NO, "Already exist");
+    return io_completion_response (command, RESP_NO, "Already exist");
 
  /* Allocates memory.  */
   name = namespace_getfullpath (name, delim, NULL);
   if (!name)
-    return util_finish (command, RESP_NO, "Cannot remove");
+    return io_completion_response (command, RESP_NO, "Cannot remove");
 
   if (remove (name) != 0)
     {
       rc = RESP_NO;
       msg = "Cannot remove";
     }
-  return util_finish (command, rc, msg);
+  return io_completion_response (command, rc, msg);
 }
diff --git a/imap4d/examine.c b/imap4d/examine.c
index 6f5ed6a..cd4d669 100644
--- a/imap4d/examine.c
+++ b/imap4d/examine.c
@@ -36,7 +36,7 @@ int
 imap4d_examine (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
                         MU_STREAM_READ);
 }
diff --git a/imap4d/expunge.c b/imap4d/expunge.c
index efea04f..46df985 100644
--- a/imap4d/expunge.c
+++ b/imap4d/expunge.c
@@ -36,11 +36,11 @@ int
 imap4d_expunge (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   /* FIXME: check for errors.  */
   mu_mailbox_expunge (mbox);
 
   imap4d_sync ();
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index 958703c..618409b 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -76,7 +76,7 @@ fetch_send_address (const char *addr)
   /* Short circuit.  */
   if (addr == NULL || *addr == '\0')
     {
-      util_send ("NIL");
+      io_sendf ("NIL");
       return RESP_OK;
     }
 
@@ -86,26 +86,26 @@ fetch_send_address (const char *addr)
   /* We failed: can't parse.  */
   if (count == 0)
     {
-      util_send ("NIL");
+      io_sendf ("NIL");
       return RESP_OK;
     }
 
-  util_send ("(");
+  io_sendf ("(");
   for (i = 1; i <= count; i++)
     {
       const char *str;
       int is_group = 0;
 
-      util_send ("(");
+      io_sendf ("(");
 
       mu_address_sget_personal (address, i, &str);
-      util_send_qstring (str);
-      util_send (" ");
+      io_send_qstring (str);
+      io_sendf (" ");
 
       mu_address_sget_route (address, i, &str);
-      util_send_qstring (str);
+      io_send_qstring (str);
 
-      util_send (" ");
+      io_sendf (" ");
 
       mu_address_is_group (address, i, &is_group);
       str = NULL;
@@ -114,16 +114,16 @@ fetch_send_address (const char *addr)
       else
        mu_address_sget_local_part (address, i, &str);
 
-      util_send_qstring (str);
+      io_send_qstring (str);
 
-      util_send (" ");
+      io_sendf (" ");
 
       mu_address_sget_domain (address, i, &str);
-      util_send_qstring (str);
+      io_send_qstring (str);
 
-      util_send (")");
+      io_sendf (")");
     }
-  util_send (")");
+  io_sendf (")");
   return RESP_OK;
 }
 
@@ -134,16 +134,16 @@ fetch_send_header_value (mu_header_t header, const char 
*name,
   char *buffer;
   
   if (space)
-    util_send (" ");
+    io_sendf (" ");
   if (mu_header_aget_value (header, name, &buffer) == 0)
     {
-      util_send_qstring (buffer);
+      io_send_qstring (buffer);
       free (buffer);
     }
   else if (defval)
-    util_send_qstring (defval);
+    io_send_qstring (defval);
   else
-    util_send ("NIL");
+    io_sendf ("NIL");
 }
 
 static void
@@ -153,7 +153,7 @@ fetch_send_header_address (mu_header_t header, const char 
*name,
   char *buffer;
   
   if (space)
-    util_send (" ");
+    io_sendf (" ");
   if (mu_header_aget_value (header, name, &buffer) == 0)
     {
       fetch_send_address (buffer);
@@ -172,36 +172,36 @@ send_parameter_list (const char *buffer)
   
   if (!buffer)
     {
-      util_send ("NIL");
+      io_sendf ("NIL");
       return;
     }
 
   mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
   
   if (argc == 0)
-    util_send ("NIL");
+    io_sendf ("NIL");
   else
     {
       char *p;
       
-      util_send ("(");
+      io_sendf ("(");
         
       p = argv[0];
-      util_send_qstring (p);
+      io_send_qstring (p);
 
       if (argc > 1)
        {
          int i, space = 0;
          char *lvalue = NULL;
 
-         util_send ("(");
+         io_sendf ("(");
          for (i = 1; i < argc; i++)
            {
              if (lvalue)
                {
                  if (space)
-                   util_send (" ");
-                 util_send_qstring (lvalue);
+                   io_sendf (" ");
+                 io_send_qstring (lvalue);
                  lvalue = NULL;
                  space = 1;
                }
@@ -215,8 +215,8 @@ send_parameter_list (const char *buffer)
                  if (++i < argc)
                    {
                      char *p = argv[i];
-                     util_send (" ");
-                     util_send_qstring (p);
+                     io_sendf (" ");
+                     io_send_qstring (p);
                    }
                  break;
                  
@@ -227,14 +227,14 @@ send_parameter_list (const char *buffer)
          if (lvalue)
            {
              if (space)
-               util_send (" ");
-             util_send_qstring (lvalue);
+               io_sendf (" ");
+             io_send_qstring (lvalue);
            }
-         util_send (")");
+         io_sendf (")");
        }
       else
-       util_send (" NIL");
-      util_send (")");
+       io_sendf (" NIL");
+      io_sendf (")");
     }
   mu_argcv_free (argc, argv);
 }
@@ -246,7 +246,7 @@ fetch_send_header_list (mu_header_t header, const char 
*name,
   char *buffer;
   
   if (space)
-    util_send (" ");
+    io_sendf (" ");
   if (mu_header_aget_value (header, name, &buffer) == 0)
     {
       send_parameter_list (buffer);
@@ -255,7 +255,7 @@ fetch_send_header_list (mu_header_t header, const char 
*name,
   else if (defval)
     send_parameter_list (defval);
   else
-    util_send ("NIL");
+    io_sendf ("NIL");
 }
 
 /* ENVELOPE:
@@ -281,7 +281,7 @@ fetch_envelope0 (mu_message_t msg)
 
   /* From:  */
   mu_header_aget_value (header, "From", &from);
-  util_send (" ");
+  io_sendf (" ");
   fetch_send_address (from);
 
   fetch_send_header_address (header, "Sender", from, 1);
@@ -368,9 +368,9 @@ bodystructure (mu_message_t msg, int extension)
       if (s)
        *s++ = 0;
       p = argv[0];
-      util_send_qstring (p);
-      util_send (" ");
-      util_send_qstring (s);
+      io_send_qstring (p);
+      io_sendf (" ");
+      io_send_qstring (s);
 
       /* body parameter parenthesized list: Content-type attributes */
       if (argc > 1 || text_plain)
@@ -380,7 +380,7 @@ bodystructure (mu_message_t msg, int extension)
          int have_charset = 0;
          int i;
          
-         util_send (" (");
+         io_sendf (" (");
          for (i = 1; i < argc; i++)
            {
              /* body parameter parenthesized list:
@@ -388,8 +388,8 @@ bodystructure (mu_message_t msg, int extension)
              if (lvalue)
                {
                  if (space)
-                   util_send (" ");
-                 util_send_qstring (lvalue);
+                   io_sendf (" ");
+                 io_send_qstring (lvalue);
                  lvalue = NULL;
                  space = 1;
                }
@@ -403,8 +403,8 @@ bodystructure (mu_message_t msg, int extension)
                  if (++i < argc)
                    {
                      char *p = argv[i];
-                     util_send (" ");
-                     util_send_qstring (p);
+                     io_sendf (" ");
+                     io_send_qstring (p);
                    }
                  break;
                  
@@ -419,27 +419,27 @@ bodystructure (mu_message_t msg, int extension)
          if (lvalue)
            {
              if (space)
-               util_send (" ");
-             util_send_qstring (lvalue);
+               io_sendf (" ");
+             io_send_qstring (lvalue);
            }
          
          if (!have_charset && text_plain)
            {
              if (space)
-               util_send (" ");
-             util_send ("\"CHARSET\" \"US-ASCII\"");
+               io_sendf (" ");
+             io_sendf ("\"CHARSET\" \"US-ASCII\"");
            }
-         util_send (")");
+         io_sendf (")");
        }
       else
-       util_send (" NIL");
+       io_sendf (" NIL");
       mu_argcv_free (argc, argv);
       free (buffer);
     }
   else
     {
       /* Default? If Content-Type is not present consider as text/plain.  */
-      util_send ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\")");
+      io_sendf ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\")");
       text_plain = 1;
     }
   
@@ -459,14 +459,14 @@ bodystructure (mu_message_t msg, int extension)
     mu_message_get_body (msg, &body);
     mu_body_size (body, &size);
     mu_body_lines (body, &blines);
-    util_send (" %s", mu_umaxtostr (0, size + blines));
+    io_sendf (" %s", mu_umaxtostr (0, size + blines));
   }
 
   /* If the mime type was text.  */
   if (text_plain)
     {
       /* Add the line number of the body.  */
-      util_send (" %s", mu_umaxtostr (0, blines));
+      io_sendf (" %s", mu_umaxtostr (0, blines));
     }
   else if (message_rfc822)
     {
@@ -474,16 +474,16 @@ bodystructure (mu_message_t msg, int extension)
       mu_message_t emsg = NULL;
       mu_message_unencapsulate  (msg, &emsg, NULL);
       /* Add envelope structure of the encapsulated message.  */
-      util_send (" (");
+      io_sendf (" (");
       fetch_envelope0 (emsg);
-      util_send (")");
+      io_sendf (")");
       /* Add body structure of the encapsulated message.  */
-      util_send ("(");
+      io_sendf ("(");
       bodystructure (emsg, extension);
-      util_send (")");
+      io_sendf (")");
       /* Size in text lines of the encapsulated message.  */
       mu_message_lines (emsg, &lines);
-      util_send (" %s", mu_umaxtostr (0, lines));
+      io_sendf (" %s", mu_umaxtostr (0, lines));
       mu_message_destroy (&emsg, NULL);
     }
 
@@ -543,9 +543,9 @@ fetch_bodystructure0 (mu_message_t message, int extension)
         {
           mu_message_t msg = NULL;
           mu_message_get_part (message, i, &msg);
-          util_send ("(");
+          io_sendf ("(");
           fetch_bodystructure0 (msg, extension);
-          util_send (")");
+          io_sendf (")");
         } /* for () */
 
       mu_message_get_header (message, &header);
@@ -563,8 +563,8 @@ fetch_bodystructure0 (mu_message_t message, int extension)
          s = strchr (argv[0], '/');
          if (s)
            s++;
-         util_send (" ");
-         util_send_qstring (s);
+         io_sendf (" ");
+         io_send_qstring (s);
 
          /* The extension data for multipart. */
          if (extension)
@@ -572,7 +572,7 @@ fetch_bodystructure0 (mu_message_t message, int extension)
              int space = 0;
              char *lvalue = NULL;
              
-             util_send (" (");
+             io_sendf (" (");
              for (i = 1; i < argc; i++)
                {
                  /* body parameter parenthesized list:
@@ -580,8 +580,8 @@ fetch_bodystructure0 (mu_message_t message, int extension)
                  if (lvalue)
                    {
                      if (space)
-                       util_send (" ");
-                     util_send_qstring (lvalue);
+                       io_sendf (" ");
+                     io_send_qstring (lvalue);
                      lvalue = NULL;
                      space = 1;
                    }
@@ -595,8 +595,8 @@ fetch_bodystructure0 (mu_message_t message, int extension)
                      if (++i < argc)
                        {
                          char *p = argv[i];
-                         util_send (" ");
-                         util_send_qstring (p);
+                         io_sendf (" ");
+                         io_send_qstring (p);
                        }
                      break;
                      
@@ -607,19 +607,19 @@ fetch_bodystructure0 (mu_message_t message, int extension)
              if (lvalue)
                {
                  if (space)
-                   util_send (" ");
-                 util_send_qstring (lvalue);
+                   io_sendf (" ");
+                 io_send_qstring (lvalue);
                }
-             util_send (")");
+             io_sendf (")");
            }
          else
-           util_send (" NIL");
+           io_sendf (" NIL");
          mu_argcv_free (argc, argv);
           free (buffer);
        }
       else
        /* No content-type header */
-       util_send (" NIL");
+       io_sendf (" NIL");
 
       /* body disposition: Content-Disposition.  */
       fetch_send_header_list (header, MU_HEADER_CONTENT_DISPOSITION,
@@ -643,7 +643,7 @@ set_seen (struct fetch_function_closure *ffc,
       mu_message_get_attribute (frt->msg, &attr);
       if (!mu_attribute_is_read (attr))
        {
-         util_send ("FLAGS (\\Seen) ");
+         io_sendf ("FLAGS (\\Seen) ");
          mu_attribute_set_read (attr);
        }
     }
@@ -668,21 +668,21 @@ fetch_send_section_part (struct fetch_function_closure 
*ffc,
 {
   int i;
   
-  util_send ("BODY[");
+  io_sendf ("BODY[");
   for (i = 0; i < ffc->nset; i++)
     {
       if (i)
-       util_send (".");
-      util_send ("%lu",  (unsigned long) ffc->section_part[i]);
+       io_sendf (".");
+      io_sendf ("%lu",  (unsigned long) ffc->section_part[i]);
     }
   if (suffix)
     {
       if (i)
-       util_send (".");
-      util_send ("%s", suffix);
+       io_sendf (".");
+      io_sendf ("%s", suffix);
     }
   if (close_bracket)
-    util_send ("]");
+    io_sendf ("]");
 }
 
 static int
@@ -707,17 +707,17 @@ fetch_io (mu_stream_t stream, size_t start, size_t size, 
size_t max)
        }
       if (max)
        {
-         util_send (" {%lu}\n", (unsigned long) max);
-         util_copy_out (rfc, max);
+         io_sendf (" {%lu}\n", (unsigned long) max);
+         io_copy_out (rfc, max);
          /* FIXME: Make sure exactly max bytes were sent */
        }
       else
-       util_send (" \"\"");
+       io_sendf (" \"\"");
     }
   else if (start > max)
     {
-      util_send ("<%lu>", (unsigned long) start);
-      util_send (" \"\"");
+      io_sendf ("<%lu>", (unsigned long) start);
+      io_sendf (" \"\"");
     }
   else if (size + 2 < size) /* Check for integer overflow */
     {
@@ -751,14 +751,14 @@ fetch_io (mu_stream_t stream, size_t start, size_t size, 
size_t max)
          p += n;
        }
       *p = 0;
-      util_send ("<%lu>", (unsigned long) start);
+      io_sendf ("<%lu>", (unsigned long) start);
       if (total)
        {
-         util_send (" {%lu}\n", (unsigned long) total);
-         util_send_bytes (buffer, total);
+         io_sendf (" {%lu}\n", (unsigned long) total);
+         io_send_bytes (buffer, total);
        }
       else
-       util_send (" \"\"");
+       io_sendf (" \"\"");
       free (buffer);
     }
   mu_stream_destroy (&rfc);
@@ -774,7 +774,7 @@ _frt_uid (struct fetch_function_closure *ffc,
   size_t uid = 0;
 
   mu_message_get_uid (frt->msg, &uid);
-  util_send ("%s %s", ffc->name, mu_umaxtostr (0, uid));
+  io_sendf ("%s %s", ffc->name, mu_umaxtostr (0, uid));
   return RESP_OK;
 }
 
@@ -782,9 +782,9 @@ static int
 _frt_envelope (struct fetch_function_closure *ffc,
               struct fetch_runtime_closure *frt)
 {
-  util_send ("%s (", ffc->name);
+  io_sendf ("%s (", ffc->name);
   fetch_envelope0 (frt->msg);
-  util_send (")");
+  io_sendf (")");
   return RESP_OK;
 }
 
@@ -795,9 +795,9 @@ _frt_flags (struct fetch_function_closure *ffc,
   mu_attribute_t attr = NULL;
 
   mu_message_get_attribute (frt->msg, &attr);
-  util_send ("%s (", ffc->name);
+  io_sendf ("%s (", ffc->name);
   util_print_flags (attr);
-  util_send (")");
+  io_sendf (")");
   return 0;
 }
 
@@ -850,8 +850,8 @@ _frt_internaldate (struct fetch_function_closure *ffc,
       tmp = localtime (&t);
     }
   mu_strftime (datebuf, sizeof (datebuf), "%d-%b-%Y %H:%M:%S", tmp);
-  util_send ("%s", ffc->name);
-  util_send (" \"%s +0000\"", datebuf);
+  io_sendf ("%s", ffc->name);
+  io_sendf (" \"%s +0000\"", datebuf);
   return 0;
 }
 
@@ -859,9 +859,9 @@ static int
 _frt_bodystructure (struct fetch_function_closure *ffc,
                    struct fetch_runtime_closure *frt)
 {
-  util_send ("%s (", ffc->name);
+  io_sendf ("%s (", ffc->name);
   fetch_bodystructure0 (frt->msg, 1); /* 1 means with extension data.  */
-  util_send (")");
+  io_sendf (")");
   return RESP_OK;
 }
 
@@ -869,9 +869,9 @@ static int
 _frt_bodystructure0 (struct fetch_function_closure *ffc,
                     struct fetch_runtime_closure *frt)
 {
-  util_send ("%s (", ffc->name);
+  io_sendf ("%s (", ffc->name);
   fetch_bodystructure0 (frt->msg, 0);
-  util_send (")");
+  io_sendf (")");
   return RESP_OK;
 }
 
@@ -887,13 +887,13 @@ _frt_body (struct fetch_function_closure *ffc,
   
   set_seen (ffc, frt);
   if (ffc->name)
-    util_send ("%s", ffc->name);
+    io_sendf ("%s", ffc->name);
   else
     fetch_send_section_part (ffc, NULL, 1);
   msg = fetch_get_part (ffc, frt);
   if (!msg)
     {
-      util_send (" \"\"");
+      io_sendf (" \"\"");
       return RESP_OK;
     }
   mu_message_get_streamref (msg, &stream);
@@ -916,13 +916,13 @@ _frt_body_text (struct fetch_function_closure *ffc,
   
   set_seen (ffc, frt);
   if (ffc->name)
-    util_send ("%s",  ffc->name);
+    io_sendf ("%s",  ffc->name);
   else
     fetch_send_section_part (ffc, "TEXT", 1);
   msg = fetch_get_part (ffc, frt);
   if (!msg)
     {
-      util_send (" \"\"");
+      io_sendf (" \"\"");
       return RESP_OK;
     }
 
@@ -944,7 +944,7 @@ _frt_size (struct fetch_function_closure *ffc,
   
   mu_message_size (frt->msg, &size);
   mu_message_lines (frt->msg, &lines);
-  util_send ("%s %lu", ffc->name, (unsigned long) (size + lines));
+  io_sendf ("%s %lu", ffc->name, (unsigned long) (size + lines));
   return RESP_OK;
 }
 
@@ -961,14 +961,14 @@ _frt_header0 (struct fetch_function_closure *ffc,
   
   set_seen (ffc, frt);
   if (ffc->name)
-    util_send ("%s",  ffc->name);
+    io_sendf ("%s",  ffc->name);
   else
     fetch_send_section_part (ffc, suffix, 1);
 
   msg = fetch_get_part (ffc, frt);
   if (!msg)
     {
-      util_send (" \"\"");
+      io_sendf (" \"\"");
       return RESP_OK;
     }
   mu_message_get_header (msg, &header);
@@ -999,10 +999,10 @@ _send_header_name (void *item, void *data)
 {
   int *pf = data;
   if (*pf)
-    util_send (" ");
+    io_sendf (" ");
   else
     *pf = 1;
-  util_send ("%s", (char*) item);
+  io_sendf ("%s", (char*) item);
   return 0;
 }
 
@@ -1031,16 +1031,16 @@ _frt_header_fields (struct fetch_function_closure *ffc,
 
   fetch_send_section_part (ffc, "HEADER.FIELDS", 0);
   if (ffc->not)
-    util_send (".NOT");
-  util_send (" (");
+    io_sendf (".NOT");
+  io_sendf (" (");
   status = 0;
   mu_list_do (ffc->headers, _send_header_name, &status);
-  util_send (")]");
+  io_sendf (")]");
   
   msg = fetch_get_part (ffc, frt);
   if (!msg)
     {
-      util_send (" \"\"");
+      io_sendf (" \"\"");
       return RESP_OK;
     }
 
@@ -1048,7 +1048,7 @@ _frt_header_fields (struct fetch_function_closure *ffc,
   if (mu_message_get_header (msg, &header)
       || mu_header_get_iterator (header, &itr))
     {
-      util_send (" \"\"");
+      io_sendf (" \"\"");
       return RESP_OK;
     }
 
@@ -1113,7 +1113,7 @@ _do_fetch (void *item, void *data)
   struct fetch_function_closure *ffc = item;
   struct fetch_runtime_closure *frt = data;
   if (frt->eltno++)
-    util_send (" ");
+    io_sendf (" ");
   return ffc->fun (ffc, frt);
 }
 
@@ -1653,10 +1653,10 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char 
**err_text)
          if (frc.msgno &&
              mu_mailbox_get_message (mbox, frc.msgno, &frc.msg) == 0)
            {
-             util_send ("* %lu FETCH (", (unsigned long) frc.msgno);
+             io_sendf ("* %lu FETCH (", (unsigned long) frc.msgno);
              frc.eltno = 0;
              rc = mu_list_do (pclos.fnlist, _do_fetch, &frc);
-             util_send (")\n");
+             io_sendf (")\n");
            }
        }
       }
@@ -1690,5 +1690,5 @@ imap4d_fetch (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   char *err_text = "Completed";
 
   rc = imap4d_fetch0 (tok, 0, &err_text);
-  return util_finish (command, rc, "%s", err_text);
+  return io_completion_response (command, rc, "%s", err_text);
 }
diff --git a/imap4d/id.c b/imap4d/id.c
index 353d3fe..2561a1a 100644
--- a/imap4d/id.c
+++ b/imap4d/id.c
@@ -169,7 +169,7 @@ imap4d_id (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
 {
   int rc = eat_args (tok);
   if (rc != RESP_OK)
-    return util_finish (command, rc, "Syntax error");
+    return io_completion_response (command, rc, "Syntax error");
   if (imap4d_id_list)
     {
       mu_iterator_t itr;
@@ -194,15 +194,15 @@ imap4d_id (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
          if (q)
            {
              if (outcnt++ == 0)
-               util_send ("* ID (");
+               io_sendf ("* ID (");
              else
-               util_send (" ");
-             util_send ("\"%*.*s\" \"%s\"", (int) len, (int) len, p, q);
+               io_sendf (" ");
+             io_sendf ("\"%*.*s\" \"%s\"", (int) len, (int) len, p, q);
            }
        }
       mu_iterator_destroy (&itr);
       if (outcnt)
-       util_send (")\n");
+       io_sendf (")\n");
     }
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/idle.c b/imap4d/idle.c
index e8e7b8c..fde91ab 100644
--- a/imap4d/idle.c
+++ b/imap4d/idle.c
@@ -27,21 +27,20 @@ imap4d_idle (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   size_t token_size = 0, token_len;
   
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
-  if (util_wait_input (0) == -1)
-    return util_finish (command, RESP_NO, "Cannot idle");
+  if (io_wait_input (0) == -1)
+    return io_completion_response (command, RESP_NO, "Cannot idle");
 
-  util_send ("+ idling\n");
-  util_flush_output ();
+  io_sendf ("+ idling\n");
+  io_flush ();
 
   start = time (NULL);
   while (1)
     {
-      if (util_wait_input (5))
+      if (io_wait_input (5))
        {
-          imap4d_getline (&token_str, &token_size, &token_len);          
-         token_len = util_trim_nl (token_str, token_len);
+          io_getline (&token_str, &token_size, &token_len);      
          if (token_len == 4 && mu_c_strcasecmp (token_str, "done") == 0)
            break;
        }
@@ -49,9 +48,9 @@ imap4d_idle (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
        imap4d_bye (ERR_TIMEOUT);
 
       imap4d_sync ();
-      util_flush_output ();
+      io_flush ();
     }
   free (token_str);
-  return util_finish (command, RESP_OK, "terminated");
+  return io_completion_response (command, RESP_OK, "terminated");
 }
 
diff --git a/imap4d/imap4d.c b/imap4d/imap4d.c
index ea492a0..1df3965 100644
--- a/imap4d/imap4d.c
+++ b/imap4d/imap4d.c
@@ -399,7 +399,7 @@ imap4d_mainloop (int fd, FILE *infile, FILE *outfile)
   int debug_mode = isatty (fd);
 
   imap4d_child_signal_setup (imap4d_child_signal);
-  util_setio (infile, outfile);
+  io_setio (infile, outfile);
 
   if (imap4d_preauth_setup (fd) == 0)
     {
@@ -413,13 +413,14 @@ imap4d_mainloop (int fd, FILE *infile, FILE *outfile)
     }
   else
     {
-      util_flush_output ();
+      io_flush ();
       return 0;
     }
 
   /* Greetings.  */
-  util_out ((state == STATE_AUTH) ? RESP_PREAUTH : RESP_OK, "%s", text);
-  util_flush_output ();
+  io_untagged_response ((state == STATE_AUTH) ? 
+                        RESP_PREAUTH : RESP_OK, "%s", text);
+  io_flush ();
 
   tokp = imap4d_tokbuf_init ();
   while (1)
@@ -429,7 +430,7 @@ imap4d_mainloop (int fd, FILE *infile, FILE *outfile)
       imap4d_sync ();
       util_do_command (tokp);
       imap4d_sync ();
-      util_flush_output ();
+      io_flush ();
     }
 
   return 0;
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index 17e7ef6..ba6ef65 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -154,7 +154,8 @@ struct imap4d_command
 #define ERR_TLS               6
 #define ERR_MAILBOX_CORRUPTED 7
 #define ERR_TERMINATE         8
-  
+#define ERR_STREAM_CREATE     9
+
 /* Namespace numbers */
 #define NS_PRIVATE 0
 #define NS_OTHER   1
@@ -208,14 +209,33 @@ extern char *strtok_r (char *s, const char *delim, char 
**save_ptr);
 #endif
 
 /* Input functions */
+extern mu_stream_t iostream;
+extern int  io_untagged_response (int, const char *, ...) MU_PRINTFLIKE(2,3);
+extern int  io_sendf (const char *, ...) MU_PRINTFLIKE(1,2);
+extern int  io_send_bytes (const char *buf, size_t size);
+extern int  io_send_qstring (const char *);
+extern int  io_send_literal (const char *);
+extern int  io_copy_out (mu_stream_t str, size_t size);
+extern int  io_completion_response (struct imap4d_command *, int,
+                                    const char *, ...) MU_PRINTFLIKE(3,4);
+extern int io_stream_completion_response (mu_stream_t str,
+                                         struct imap4d_command *command,
+                                         int rc, 
+                                         const char *format, ...)
+                                    MU_PRINTFLIKE(4,5);
+int io_getline (char **pbuf, size_t *psize, size_t *pnbytes);
+void io_setio (FILE*, FILE*);
+void io_flush (void);
+int io_wait_input (int);
+  
 imap4d_tokbuf_t imap4d_tokbuf_init (void);
 void imap4d_tokbuf_destroy (imap4d_tokbuf_t *tok);
 int imap4d_tokbuf_argc (imap4d_tokbuf_t tok);
 char *imap4d_tokbuf_getarg (imap4d_tokbuf_t tok, int n);
 void imap4d_readline (imap4d_tokbuf_t tok);
-struct imap4d_tokbuf *imap4d_tokbuf_from_string (char *str);
-int imap4d_getline (char **pbuf, size_t *psize, size_t *pnbytes);
+imap4d_tokbuf_t imap4d_tokbuf_from_string (char *str);
 
+  
 #define IMAP4_ARG_TAG     0
 #define IMAP4_ARG_COMMAND 1
 #define IMAP4_ARG_1       2
@@ -328,18 +348,10 @@ void imap4d_child_signal_setup (RETSIGTYPE (*handler) 
(int signo));
 extern void imap4d_capability_add (const char *str);
 extern void imap4d_capability_remove (const char *str);
 extern void imap4d_capability_init (void);
-  
+
 /* Helper functions.  */
-extern int  util_out (int, const char *, ...) MU_PRINTFLIKE(2,3);
-extern int  util_send (const char *, ...) MU_PRINTFLIKE(1,2);
-extern int  util_send_bytes (const char *buf, size_t size);
-extern int  util_send_qstring (const char *);
-extern int  util_send_literal (const char *);
-extern int  util_copy_out (mu_stream_t str, size_t size);
 
 extern int  util_start (char *);
-extern int  util_finish (struct imap4d_command *, int, const char *, ...) 
-                         MU_PRINTFLIKE(3,4);
 extern int  util_getstate (void);
 extern int  util_do_command (imap4d_tokbuf_t);
 extern char *util_tilde_expansion (const char *, const char *);
@@ -361,18 +373,6 @@ int util_type_to_attribute (int type, char **attr_str);
 int util_attribute_matches_flag (mu_attribute_t attr, const char *item);
 int util_uidvalidity (mu_mailbox_t smbox, unsigned long *uidvp);
 
-void util_setio (FILE*, FILE*);
-void util_flush_output (void);
-void util_get_input (mu_stream_t *pstr);
-void util_get_output (mu_stream_t *pstr);
-void util_set_input (mu_stream_t str);
-void util_set_output (mu_stream_t str);
-int util_wait_input (int);
-  
-void util_register_event (int old_state, int new_state,
-                         mu_list_action_t *action, void *data);
-void util_event_remove (void *id);
-void util_run_events (int old_state, int new_state);
   
 int util_is_master (void);
 void util_bye (void);  
@@ -386,8 +386,26 @@ int util_trim_nl (char *s, size_t len);
 int imap4d_init_tls_server (void);
 #endif /* WITH_TLS */
 
-typedef int (*imap4d_auth_handler_fp) (struct imap4d_command *,
-                                      char *, char **);
+struct imap4d_auth
+{
+  /* input */
+  struct imap4d_command *command;
+  char *auth_type;
+  /* output */
+  char *username;
+  int response;
+};
+
+enum imap4d_auth_result
+  {
+    imap4d_auth_nosup,
+    imap4d_auth_ok,
+    imap4d_auth_resp,
+    imap4d_auth_fail
+  };
+  
+typedef enum imap4d_auth_result
+          (*imap4d_auth_handler_fp) (struct imap4d_auth *);
   
 extern void auth_add (char *name, imap4d_auth_handler_fp handler);
 extern void auth_remove (char *name);
diff --git a/imap4d/io.c b/imap4d/io.c
new file mode 100644
index 0000000..05645ae
--- /dev/null
+++ b/imap4d/io.c
@@ -0,0 +1,616 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+   2009, 2010 Free Software Foundation, Inc.
+
+   GNU Mailutils 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 3, or (at your option)
+   any later version.
+
+   GNU Mailutils 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 GNU Mailutils; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301 USA */
+
+#include "imap4d.h"
+
+mu_stream_t iostream;
+
+void
+io_setio (FILE *in, FILE *out)
+{
+  mu_stream_t str, istream, ostream;
+  
+  if (!in)
+    imap4d_bye (ERR_NO_IFILE);
+  if (!out)
+    imap4d_bye (ERR_NO_OFILE);
+
+  if (mu_stdio_stream_create (&istream, fileno (in), 
+                              MU_STREAM_READ | MU_STREAM_AUTOCLOSE))
+    imap4d_bye (ERR_STREAM_CREATE);
+  mu_stream_set_buffer (istream, mu_buffer_line, 1024);
+  
+  if (mu_stdio_stream_create (&ostream, fileno (out), 
+                              MU_STREAM_WRITE | MU_STREAM_AUTOCLOSE))
+    imap4d_bye (ERR_STREAM_CREATE);
+
+  /* Combine the two streams into an I/O one. */
+  if (mu_iostream_create (&str, istream, ostream))
+    imap4d_bye (ERR_STREAM_CREATE);
+
+  /* Convert all writes to CRLF form.
+     There is no need to convert reads, as the code ignores extra \r anyway.
+  */
+  if (mu_filter_create (&iostream, str, "CRLF", MU_FILTER_ENCODE,
+                       MU_STREAM_WRITE | MU_STREAM_RDTHRU))
+    imap4d_bye (ERR_STREAM_CREATE);
+  /* Change buffering scheme: filter streams are fully buffered by default. */
+  mu_stream_set_buffer (iostream, mu_buffer_line, 1024);
+  
+  if (imap4d_transcript)
+    {
+      int rc;
+      mu_debug_t debug;
+      mu_stream_t dstr, xstr;
+      
+      mu_diag_get_debug (&debug);
+      
+      rc = mu_dbgstream_create (&dstr, debug, MU_DIAG_DEBUG, 0);
+      if (rc)
+       mu_error (_("cannot create debug stream; transcript disabled: %s"),
+                 mu_strerror (rc));
+      else
+       {
+         rc = mu_xscript_stream_create (&xstr, iostream, dstr, NULL);
+         if (rc)
+           mu_error (_("cannot create transcript stream: %s"),
+                     mu_strerror (rc));
+         else
+           {
+             mu_stream_unref (iostream);
+             iostream = xstr;
+           }
+       }
+    }
+}
+
+#ifdef WITH_TLS
+int
+imap4d_init_tls_server ()
+{
+  mu_stream_t tlsstream, stream[2];
+  int rc;
+
+  stream[0] = stream[1] = NULL;
+  rc = mu_stream_ioctl (iostream, MU_IOCTL_SWAP_STREAM, stream);
+  if (rc)
+    {
+      mu_error (_("%s failed: %s"), "MU_IOCTL_SWAP_STREAM",
+               mu_stream_strerror (iostream, rc));
+      return 1;
+    }
+  
+  rc = mu_tls_server_stream_create (&tlsstream, stream[0], stream[1], 0);
+  if (rc)
+    return 1;
+
+  rc = mu_stream_open (tlsstream);
+  if (rc)
+    {
+      mu_diag_output (MU_DIAG_ERROR, _("cannot open TLS stream: %s"),
+                     mu_stream_strerror (tlsstream, rc));
+      mu_stream_destroy (&tlsstream);
+      return 1;
+    }
+  else
+    stream[0] = stream[1] = tlsstream;
+
+  rc = mu_stream_ioctl (iostream, MU_IOCTL_SWAP_STREAM, stream);
+  if (rc)
+    {
+      mu_error (_("%s failed: %s"), "MU_IOCTL_SWAP_STREAM",
+               mu_stream_strerror (iostream, rc));
+      imap4d_bye (ERR_STREAM_CREATE);
+    }
+  return 0;
+}
+#endif
+
+
+/* Status Code to String.  */
+static const char *
+sc2string (int rc)
+{
+  switch (rc)
+    {
+    case RESP_OK:
+      return "OK ";
+
+    case RESP_BAD:
+      return "BAD ";
+
+    case RESP_NO:
+      return "NO ";
+
+    case RESP_BYE:
+      return "BYE ";
+
+    case RESP_PREAUTH:
+      return "PREAUTH ";
+    }
+  return "";
+}
+
+/* FIXME: Check return values from the output functions */
+
+int
+io_copy_out (mu_stream_t str, size_t size)
+{
+  return mu_stream_copy (iostream, str, size);
+}
+
+int
+io_send_bytes (const char *buf, size_t size)
+{
+  return mu_stream_write (iostream, buf, size, NULL);
+}
+
+int
+io_sendf (const char *format, ...)
+{
+  int status;
+  va_list ap;
+
+  va_start (ap, format);
+  status = mu_stream_vprintf (iostream, format, ap);
+  va_end (ap);
+  return status;
+}
+
+/* Send NIL if empty string, change the quoted string to a literal if the
+   string contains: double quotes, CR, LF, and '/'.  CR, LF will be change
+   to spaces.  */
+int
+io_send_qstring (const char *buffer)
+{
+  if (buffer == NULL || *buffer == '\0')
+    return io_sendf ("NIL");
+  if (strchr (buffer, '"') || strchr (buffer, '\r') || strchr (buffer, '\n')
+      || strchr (buffer, '\\'))
+    {
+      char *s;
+      int ret;
+      char *b = strdup (buffer);
+      while ((s = strchr (b, '\n')) || (s = strchr (b, '\r')))
+       *s = ' ';
+      ret = io_send_literal (b);
+      free (b);
+      return ret;
+    }
+  return io_sendf ("\"%s\"", buffer);
+}
+
+int
+io_send_literal (const char *buffer)
+{
+  return io_sendf ("{%lu}\n%s", (unsigned long) strlen (buffer), buffer);
+}
+
+/* Send an untagged response.  */
+int
+io_untagged_response (int rc, const char *format, ...)
+{
+  int status;
+  va_list ap;
+
+  mu_stream_printf (iostream, "* %s", sc2string (rc));
+  va_start (ap, format);
+  status = mu_stream_vprintf (iostream, format, ap);
+  va_end (ap);
+  mu_stream_write (iostream, "\n", 1, NULL);
+  return status;
+}
+
+/* Send the completion response and reset the state.  */
+int
+io_format_completion_response (mu_stream_t str,
+                              struct imap4d_command *command, int rc, 
+                              const char *format, va_list ap)
+{
+  int new_state;
+  int status = 0;
+  const char *sc = sc2string (rc);
+
+  mu_stream_printf (str, "%s %s%s ",
+                   command->tag, sc, command->name);
+  mu_stream_vprintf (str, format, ap);
+  mu_stream_write (str, "\n", 1, NULL);
+
+  /* Reset the state.  */
+  if (rc == RESP_OK)
+    new_state = command->success;
+  else if (command->failure <= state)
+    new_state = command->failure;
+  else
+    new_state = STATE_NONE;
+
+  if (new_state != STATE_NONE)
+    state = new_state;
+  
+  return status;
+}
+
+int
+io_completion_response (struct imap4d_command *command, int rc, 
+                        const char *format, ...)
+{
+  va_list ap;
+  int status;
+
+  va_start (ap, format);
+  status = io_format_completion_response (iostream, command, rc, format, ap);
+  va_end (ap);
+  return status;
+}
+
+int
+io_stream_completion_response (mu_stream_t str,
+                              struct imap4d_command *command, int rc, 
+                              const char *format, ...)
+{
+  va_list ap;
+  int status;
+  
+  va_start (ap, format);
+  status = io_format_completion_response (str, command, rc, format, ap);
+  va_end (ap);
+  return status;
+}
+
+/* Wait TIMEOUT seconds for data on the input stream.
+   Returns 0   if no data available
+           1   if some data is available
+          -1  an error occurred */
+int
+io_wait_input (int timeout)
+{
+  int wflags = MU_STREAM_READY_RD;
+  struct timeval tv;
+  int status;
+  
+  tv.tv_sec = timeout;
+  tv.tv_usec = 0;
+  status = mu_stream_wait (iostream, &wflags, &tv);
+  if (status)
+    {
+      mu_diag_output (MU_DIAG_ERROR, _("cannot poll input stream: %s"),
+                     mu_strerror(status));
+      return -1;
+    }
+  return wflags & MU_STREAM_READY_RD;
+}
+
+void
+io_flush ()
+{
+  mu_stream_flush (iostream);
+}
+
+int
+util_is_master ()
+{
+  return iostream == NULL;
+}
+
+int
+io_getline (char **pbuf, size_t *psize, size_t *pnbytes)
+{
+  size_t len;
+  int rc = mu_stream_getline (iostream, pbuf, psize, &len);
+  if (rc == 0)
+    {
+      char *s = *pbuf;
+
+      if (len == 0)
+        {
+          imap4d_bye (ERR_NO_IFILE);
+          /*FIXME rc = ECONNABORTED;*/
+        }
+      len = mu_rtrim_cset (s, "\r\n");
+      if (pnbytes)
+       *pnbytes = len;
+    }
+  return rc;
+}
+
+
+static size_t
+unquote (char *line, size_t len)
+{
+  char *prev = NULL;
+  size_t rlen = len;
+  char *p;
+  int off = 0;
+  while ((p = memchr (line + off, '\\', len - off)))
+    {
+      if (p[1] == '\\' || p[1] == '"')
+       {
+         if (prev)
+           {
+             memmove (prev, line, p - line);
+             prev += p - line;
+           }
+         else
+           prev = p;
+         off = p[1] == '\\';
+         rlen--;
+         len -= p - line + 1;
+         line = p + 1;
+       }
+    }
+  if (prev)
+    memmove (prev, line, len);
+  return rlen;
+}
+
+struct imap4d_tokbuf
+{
+  char *buffer;
+  size_t size;
+  size_t level;
+  int argc;
+  int argmax;
+  size_t *argp;
+};
+
+struct imap4d_tokbuf *
+imap4d_tokbuf_init ()
+{
+  struct imap4d_tokbuf *tok = malloc (sizeof (tok[0]));
+  if (!tok)
+    imap4d_bye (ERR_NO_MEM);
+  memset (tok, 0, sizeof (*tok));
+  return tok;
+}
+
+void
+imap4d_tokbuf_destroy (struct imap4d_tokbuf **ptok)
+{
+  struct imap4d_tokbuf *tok = *ptok;
+  free (tok->buffer);
+  free (tok->argp);
+  free (tok);
+  *ptok = NULL;
+}
+
+int
+imap4d_tokbuf_argc (struct imap4d_tokbuf *tok)
+{
+  return tok->argc;
+}
+
+char *
+imap4d_tokbuf_getarg (struct imap4d_tokbuf *tok, int n)
+{
+  if (n < tok->argc)
+    return tok->buffer + tok->argp[n];
+  return NULL;
+}
+
+static void
+imap4d_tokbuf_unquote (struct imap4d_tokbuf *tok, size_t *poff, size_t *plen)
+{
+  char *buf = tok->buffer + *poff;
+  if (buf[0] == '"' && buf[*plen - 1] == '"')
+    {
+      ++*poff;
+      *plen = unquote (buf + 1, *plen - 1);
+    }
+}
+
+static void
+imap4d_tokbuf_expand (struct imap4d_tokbuf *tok, size_t size)
+{
+  if (tok->size - tok->level < size)          
+    {                                          
+      tok->size = tok->level + size;
+      tok->buffer = realloc (tok->buffer, tok->size);
+      if (!tok->buffer)                                
+       imap4d_bye (ERR_NO_MEM);
+    }
+}
+
+#define ISDELIM(c) (strchr ("()", (c)) != NULL)
+
+static size_t
+insert_nul (struct imap4d_tokbuf *tok, size_t off)
+{
+  imap4d_tokbuf_expand (tok, 1);
+  if (off < tok->level)
+    {
+      memmove (tok->buffer + off + 1, tok->buffer + off, tok->level - off);
+      tok->level++;
+    }
+  tok->buffer[off] = 0;
+  return off + 1;
+}
+
+static size_t
+gettok (struct imap4d_tokbuf *tok, size_t off)
+{
+  char *buf = tok->buffer;
+  
+  while (off < tok->level && mu_isblank (buf[off]))
+    off++;
+
+  if (tok->argc == tok->argmax)
+    {
+      if (tok->argmax == 0)
+       tok->argmax = 16;
+      else
+       tok->argmax *= 2;
+      tok->argp = realloc (tok->argp, tok->argmax * sizeof (tok->argp[0]));
+      if (!tok->argp)
+       imap4d_bye (ERR_NO_MEM);
+    }
+  
+  if (buf[off] == '"')
+    {
+      char *start = buf + off + 1;
+      char *p = NULL;
+      
+      while (*start && (p = strchr (start, '"')))
+       {
+         if (p == start || p[-1] != '\\')
+           break;
+         start = p + 1;
+       }
+
+      if (p)
+       {
+         size_t len;
+         off++;
+         len  = unquote (buf + off, p - (buf + off));
+         buf[off + len] = 0;
+         tok->argp[tok->argc++] = off;
+         return p - buf + 1;
+       }
+    }
+
+  tok->argp[tok->argc++] = off;
+  if (ISDELIM (buf[off]))
+    return insert_nul (tok, off + 1);
+
+  while (off < tok->level && !mu_isblank (buf[off]))
+    {
+      if (ISDELIM (buf[off]))
+       return insert_nul (tok, off);
+      off++;
+    }
+  buf[off++] = 0;
+  
+  return off;
+}
+
+static void
+imap4d_tokbuf_tokenize (struct imap4d_tokbuf *tok, size_t off)
+{
+  while (off < tok->level)
+    off = gettok (tok, off);
+}
+
+static void
+check_input_err (int rc, size_t sz)
+{
+  if (rc)
+    {
+      const char *p = mu_stream_strerror (iostream, rc);
+      if (!p)
+       p = mu_strerror (rc);
+      
+      mu_diag_output (MU_DIAG_INFO,
+                     _("error reading from input file: %s"), p);
+      imap4d_bye (ERR_NO_IFILE);
+    }
+  else if (sz == 0)
+    {
+      mu_diag_output (MU_DIAG_INFO, _("unexpected eof on input"));
+      imap4d_bye (ERR_NO_IFILE);
+    }
+}
+
+static size_t
+imap4d_tokbuf_getline (struct imap4d_tokbuf *tok)
+{
+  char buffer[512];
+  size_t level = tok->level;
+  
+  do
+    {
+      size_t len;
+      int rc;
+      
+      rc = mu_stream_readline (iostream, buffer, sizeof (buffer), &len);
+      check_input_err (rc, len);
+      imap4d_tokbuf_expand (tok, len);
+      
+      memcpy (tok->buffer + tok->level, buffer, len);
+      tok->level += len;
+    }
+  while (tok->level && tok->buffer[tok->level - 1] != '\n');
+  tok->buffer[--tok->level] = 0;
+  if (tok->buffer[tok->level - 1] == '\r')
+    tok->buffer[--tok->level] = 0;
+  return level;
+}
+
+void
+imap4d_readline (struct imap4d_tokbuf *tok)
+{
+  tok->argc = 0;
+  tok->level = 0;
+  for (;;)
+    {
+      char *last_arg;
+      size_t off = imap4d_tokbuf_getline (tok);
+      imap4d_tokbuf_tokenize (tok, off);
+      if (tok->argc == 0)
+        break;  
+      last_arg = tok->buffer + tok->argp[tok->argc - 1];
+      if (last_arg[0] == '{' && last_arg[strlen(last_arg)-1] == '}')
+       {
+         int rc;
+         unsigned long number;
+         char *sp = NULL;
+         char *buf;
+         size_t len;
+         
+         number = strtoul (last_arg + 1, &sp, 10);
+         /* Client can ask for non-synchronised literal,
+            if a '+' is appended to the octet count. */
+         if (*sp == '}')
+           io_sendf ("+ GO AHEAD\n");
+         else if (*sp != '+')
+           break;
+         imap4d_tokbuf_expand (tok, number + 1);
+         off = tok->level;
+         buf = tok->buffer + off;
+          len = 0;
+          while (len < number)
+            {
+               size_t sz;
+              rc = mu_stream_read (iostream, buf + len, number - len, &sz);
+               if (rc || sz == 0)
+                 break;
+               len += sz;
+            }
+         check_input_err (rc, len);
+         imap4d_tokbuf_unquote (tok, &off, &len);
+         tok->level += len;
+         tok->buffer[tok->level++] = 0;
+         tok->argp[tok->argc - 1] = off;
+       }
+      else
+       break;
+    }
+}  
+
+struct imap4d_tokbuf *
+imap4d_tokbuf_from_string (char *str)
+{
+  struct imap4d_tokbuf *tok = imap4d_tokbuf_init ();
+  tok->buffer = strdup (str);
+  if (!tok->buffer)
+    imap4d_bye (ERR_NO_MEM);
+  tok->level = strlen (str);
+  tok->size = tok->level + 1;
+  imap4d_tokbuf_tokenize (tok, 0);
+  return tok;
+}
+
diff --git a/imap4d/list.c b/imap4d/list.c
index c7f42e4..8f1dc6d 100644
--- a/imap4d/list.c
+++ b/imap4d/list.c
@@ -51,16 +51,16 @@ list_fun (mu_folder_t folder, struct mu_list_response 
*resp, void *data)
       && memcmp (name + refinfo->homelen + 1, "INBOX", 5) == 0)
     return 0;
      
-  util_send ("* %s", "LIST (");
+  io_sendf ("* %s", "LIST (");
   if ((resp->type & (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
        == (MU_FOLDER_ATTRIBUTE_FILE|MU_FOLDER_ATTRIBUTE_DIRECTORY))
     /* nothing */;
   else if (resp->type & MU_FOLDER_ATTRIBUTE_FILE)
-    util_send ("\\NoInferiors");
+    io_sendf ("\\NoInferiors");
   else if (resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
-    util_send ("\\NoSelect");
+    io_sendf ("\\NoSelect");
   
-  util_send (") \"%c\" ", resp->separator);
+  io_sendf (") \"%c\" ", resp->separator);
 
   name = resp->name + refinfo->pfxlen;
   size = strlen (name) + refinfo->reflen + 1;
@@ -97,11 +97,11 @@ list_fun (mu_folder_t folder, struct mu_list_response 
*resp, void *data)
   name = refinfo->buf;
   
   if (strpbrk (name, "\"{}"))
-    util_send ("{%lu}\n%s\n", (unsigned long) strlen (name), name);
+    io_sendf ("{%lu}\n%s\n", (unsigned long) strlen (name), name);
   else if (is_atom (name))
-    util_send ("%s\n", name);
+    io_sendf ("%s\n", name);
   else
-    util_send ("\"%s\"\n", name);
+    io_sendf ("\"%s\"\n", name);
   return 0;
 }
 
@@ -146,7 +146,7 @@ imap4d_list (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   const char *delim = "/";
 
   if (imap4d_tokbuf_argc (tok) != 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   ref = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   wcard = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
@@ -155,14 +155,15 @@ imap4d_list (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
      return the hierarchy.  */
   if (*wcard == '\0')
     {
-      util_out (RESP_NONE, "LIST (\\NoSelect) \"%s\" \"%s\"", delim,
-               (*ref) ? delim : "");
+      io_untagged_response (RESP_NONE,
+                               "LIST (\\NoSelect) \"%s\" \"%s\"", delim,
+                              (*ref) ? delim : "");
     }
   /* There is only one mailbox in the "INBOX" hierarchy ... INBOX.  */
   else if (mu_c_strcasecmp (ref, "INBOX") == 0
           || (ref[0] == 0 && mu_c_strcasecmp (wcard, "INBOX") == 0))
     {
-      util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
+      io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
     }
   else
     {
@@ -233,16 +234,16 @@ imap4d_list (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       if (!cwd)
        {
          free (ref);
-         return util_finish (command, RESP_NO,
-                             "The requested item could not be found.");
+         return io_completion_response (command, RESP_NO,
+                                     "The requested item could not be found.");
        }
       status = mu_folder_create (&folder, cwd);
       if (status)
        {
          free (ref);
          free (cwd);
-         return util_finish (command, RESP_NO,
-                             "The requested item could not be found.");
+         return io_completion_response (command, RESP_NO,
+                                     "The requested item could not be found.");
        }
       mu_folder_set_match (folder, imap4d_match);
 
@@ -263,7 +264,7 @@ imap4d_list (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
 
       if (!*ref && (imap4d_match ("INBOX", wcard, 0) == 0
                    || imap4d_match ("inbox", wcard, 0) == 0))
-       util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
+       io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
 
       mu_folder_enumerate (folder, NULL, wcard, 0, 0, NULL,
                           list_fun, &refinfo);
@@ -273,6 +274,6 @@ imap4d_list (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       free (ref);
     }
 
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
 
diff --git a/imap4d/login.c b/imap4d/login.c
index 5a313d7..e14649c 100644
--- a/imap4d/login.c
+++ b/imap4d/login.c
@@ -38,10 +38,10 @@ imap4d_login (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   int rc;
 
   if (login_disabled || tls_required)    
-    return util_finish (command, RESP_NO, "Command disabled");
+    return io_completion_response (command, RESP_NO, "Command disabled");
 
   if (imap4d_tokbuf_argc (tok) != 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   username = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   pass = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
@@ -51,7 +51,8 @@ imap4d_login (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
   if (auth_data == NULL)
     {
       mu_diag_output (MU_DIAG_INFO, _("user `%s' nonexistent"), username);
-      return util_finish (command, RESP_NO, "User name or passwd rejected");
+      return io_completion_response (command, RESP_NO, 
+                                     "User name or passwd rejected");
     }
 
   rc = mu_authenticate (auth_data, pass);
@@ -59,11 +60,13 @@ imap4d_login (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   if (rc)
     {
       mu_diag_output (MU_DIAG_INFO, _("login failed: %s"), username);
-      return util_finish (command, RESP_NO, "User name or passwd rejected");
+      return io_completion_response (command, RESP_NO, 
+                                     "User name or passwd rejected");
     }
 
   if (imap4d_session_setup0 ())
-    return util_finish (command, RESP_NO, "User name or passwd rejected");
-  return util_finish (command, RESP_OK, "Completed");
+    return io_completion_response (command, RESP_NO, 
+                                   "User name or passwd rejected");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
 
diff --git a/imap4d/logout.c b/imap4d/logout.c
index d6ca0e0..4ce2d52 100644
--- a/imap4d/logout.c
+++ b/imap4d/logout.c
@@ -34,7 +34,7 @@ int
 imap4d_logout (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   imap4d_bye0 (OK, command);
   return 0;
 }
diff --git a/imap4d/lsub.c b/imap4d/lsub.c
index 5b748ae..6ae650e 100644
--- a/imap4d/lsub.c
+++ b/imap4d/lsub.c
@@ -42,20 +42,20 @@ imap4d_lsub (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   FILE *fp;
   
   if (imap4d_tokbuf_argc (tok) != 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   ref = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   wcard = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
 
   asprintf (&pattern, "%s%s", ref, wcard);
   if (!pattern)
-    return util_finish (command, RESP_NO, "Not enough memory");
+    return io_completion_response (command, RESP_NO, "Not enough memory");
   
   asprintf (&file, "%s/.mailboxlist", real_homedir);
   if (!file)
     {
       free (pattern);
-      return util_finish (command, RESP_NO, "Not enough memory");
+      return io_completion_response (command, RESP_NO, "Not enough memory");
     }
   
   fp = fopen (file, "r");
@@ -71,13 +71,14 @@ imap4d_lsub (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
          if (buf[len - 1] == '\n')
            buf[len - 1] = '\0';
          if (util_wcard_match (buf, pattern, delim) == 0)
-           util_out (RESP_NONE, "LIST () \"%s\" %s", delim, buf);
+           io_untagged_response (RESP_NONE, "LIST () \"%s\" %s", 
+                                    delim, buf);
        }
       fclose (fp);
       free (buf);
-      return util_finish (command, RESP_OK, "Completed");
+      return io_completion_response (command, RESP_OK, "Completed");
     }
   else if (errno == ENOENT)
-    return util_finish (command, RESP_OK, "Completed");
-  return util_finish (command, RESP_NO, "Cannot list subscriber");
+    return io_completion_response (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_NO, "Cannot list subscriber");
 }
diff --git a/imap4d/namespace.c b/imap4d/namespace.c
index 5fd1408..335a909 100644
--- a/imap4d/namespace.c
+++ b/imap4d/namespace.c
@@ -42,8 +42,8 @@ print_namespace_fun (void *item, void *data)
   const char *dir = printable_pathname (item);
   char *suf = (dir[0] && dir[strlen (dir) - 1] != '/') ? "/" : "";
   if ((*pcount)++)
-    util_send (" ");
-  util_send ("(\"%s%s\" \"/\")", dir, suf);
+    io_sendf (" ");
+  io_sendf ("(\"%s%s\" \"/\")", dir, suf);
   return 0;
 }
 
@@ -52,14 +52,14 @@ print_namespace (int nsid)
 {
   mu_list_t list = namespace[nsid];
   if (!list)
-    util_send ("NIL");
+    io_sendf ("NIL");
   else
     {
       int count;
       count = 0;
-      util_send ("(");
+      io_sendf ("(");
       mu_list_do (list, print_namespace_fun, &count);
-      util_send (")");
+      io_sendf (")");
     }
 }
 
@@ -117,18 +117,18 @@ int
 imap4d_namespace (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
-  util_send ("* NAMESPACE ");
+  io_sendf ("* NAMESPACE ");
 
   print_namespace (NS_PRIVATE);
-  util_send (" ");
+  io_sendf (" ");
   print_namespace (NS_OTHER);
-  util_send (" ");
+  io_sendf (" ");
   print_namespace (NS_SHARED);
-  util_send ("\n");
+  io_sendf ("\n");
 
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
 
 
diff --git a/imap4d/noop.c b/imap4d/noop.c
index d09d50a..cfbafd5 100644
--- a/imap4d/noop.c
+++ b/imap4d/noop.c
@@ -23,7 +23,7 @@ int
 imap4d_noop (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   imap4d_sync ();
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/rename.c b/imap4d/rename.c
index 97cfa41..a7a65cb 100644
--- a/imap4d/rename.c
+++ b/imap4d/rename.c
@@ -48,18 +48,18 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   int ns;
   
   if (imap4d_tokbuf_argc (tok) != 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   oldname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   newname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
 
   if (mu_c_strcasecmp (newname, "INBOX") == 0)
-    return util_finish (command, RESP_NO, "Name Inbox is reservered");
+    return io_completion_response (command, RESP_NO, "Name Inbox is 
reservered");
 
   /* Allocates memory.  */
   newname = namespace_getfullpath (newname, delim, &ns);
   if (!newname)
-    return util_finish (command, RESP_NO, "Permission denied");
+    return io_completion_response (command, RESP_NO, "Permission denied");
 
   /* It is an error to attempt to rename from a mailbox name that already
      exist.  */
@@ -68,7 +68,8 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       if (!S_ISDIR(newst.st_mode))
        {
          free (newname);
-         return util_finish (command, RESP_NO, "Already exist, delete first");
+         return io_completion_response (command, RESP_NO,
+                                        "Already exist, delete first");
        }
     }
 
@@ -83,7 +84,8 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       if (S_ISDIR (newst.st_mode))
        {
          free (newname);
-         return util_finish (command, RESP_NO, "Cannot be a directory");
+         return io_completion_response (command, RESP_NO, 
+                                        "Cannot be a directory");
        }
       if (mu_mailbox_create (&newmbox, newname) != 0
          || mu_mailbox_open (newmbox,
@@ -91,7 +93,8 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
                                | mailbox_mode[ns]) != 0)
        {
          free (newname);
-         return util_finish (command, RESP_NO, "Cannot create new mailbox");
+         return io_completion_response (command, RESP_NO,
+                                        "Cannot create new mailbox");
        }
       free (newname);
 
@@ -118,7 +121,7 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
        }
       mu_mailbox_close (newmbox);
       mu_mailbox_destroy (&newmbox);
-      return util_finish (command, RESP_OK, "Already exist");
+      return io_completion_response (command, RESP_OK, "Already exist");
     }
 
   oldname = namespace_getfullpath (oldname, delim, NULL);
@@ -135,5 +138,5 @@ imap4d_rename (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   if (oldname)
     free (oldname);
   free (newname);
-  return util_finish (command, rc, msg);
+  return io_completion_response (command, rc, msg);
 }
diff --git a/imap4d/search.c b/imap4d/search.c
index 1163ce9..9e4a904 100644
--- a/imap4d/search.c
+++ b/imap4d/search.c
@@ -269,7 +269,7 @@ imap4d_search (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   char *err_text= "";
   
   rc = imap4d_search0 (tok, 0, &err_text);
-  return util_finish (command, rc, "%s", err_text);
+  return io_completion_response (command, rc, "%s", err_text);
 }
   
 int
@@ -347,7 +347,7 @@ do_search (struct parsebuf *pb)
   
   mu_mailbox_messages_count (mbox, &count);
 
-  util_send ("* SEARCH");
+  io_sendf ("* SEARCH");
   for (pb->msgno = 1; pb->msgno <= count; pb->msgno++)
     {
       if (mu_mailbox_get_message (mbox, pb->msgno, &pb->msg) == 0
@@ -357,13 +357,13 @@ do_search (struct parsebuf *pb)
            {
              size_t uid;
              mu_message_get_uid (pb->msg, &uid);
-             util_send (" %s", mu_umaxtostr (0, uid));
+             io_sendf (" %s", mu_umaxtostr (0, uid));
            }
          else
-           util_send (" %s", mu_umaxtostr (0, pb->msgno));
+           io_sendf (" %s", mu_umaxtostr (0, pb->msgno));
        }
     }
-  util_send ("\n");
+  io_sendf ("\n");
 }
 
 /* Parse buffer functions */
diff --git a/imap4d/select.c b/imap4d/select.c
index 2e2142b..2897870 100644
--- a/imap4d/select.c
+++ b/imap4d/select.c
@@ -27,7 +27,7 @@ int
 imap4d_select (struct imap4d_command *command, imap4d_tokbuf_t tok)
 {
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
                         MU_STREAM_RDWR);
 }
@@ -60,7 +60,7 @@ imap4d_select0 (struct imap4d_command *command, const char 
*mboxname,
   mailbox_name = namespace_getfullpath (mboxname, "/", NULL);
 
   if (!mailbox_name)
-    return util_finish (command, RESP_NO, "Couldn't open mailbox");
+    return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
 
   if ((status = mu_mailbox_create_default (&mbox, mailbox_name)) == 0
       && (status = mu_mailbox_open (mbox, flags)) == 0)
@@ -74,14 +74,14 @@ imap4d_select0 (struct imap4d_command *command, const char 
*mboxname,
        {
          free (mailbox_name);
          /* Need to set the state explicitely for select.  */
-         return util_send ("%s OK [%s] %s Completed\n", command->tag,
-                           ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR) ?
-                           "READ-WRITE" : "READ-ONLY", command->name);
+         return io_sendf ("%s OK [%s] %s Completed\n", command->tag,
+                          ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR) ?
+                          "READ-WRITE" : "READ-ONLY", command->name);
        }
     }
   
   mu_mailbox_destroy (&mbox);
-  status = util_finish (command, RESP_NO, "Could not open %s: %s",
+  status = io_completion_response (command, RESP_NO, "Could not open %s: %s",
                        mboxname, mu_strerror (status));
   free (mailbox_name);
   return status;
@@ -109,20 +109,22 @@ imap4d_select_status ()
 
   /* This outputs EXISTS and RECENT responses */
   imap4d_sync();
-  util_out (RESP_OK, "[UIDVALIDITY %lu] UID valididy status", uidvalidity);
-  util_out (RESP_OK, "[UIDNEXT %lu] Predicted next uid",
-           (unsigned long) uidnext);
+  io_untagged_response (RESP_OK, "[UIDVALIDITY %lu] UID valididy status", 
+                           uidvalidity);
+  io_untagged_response (RESP_OK, "[UIDNEXT %lu] Predicted next uid",
+                          (unsigned long) uidnext);
   if (unseen)
-    util_out (RESP_OK, "[UNSEEN %lu] first unseen messsage ",
-             (unsigned long) unseen);
-  util_out (RESP_NONE, "FLAGS (%s)", mflags);
+    io_untagged_response (RESP_OK, "[UNSEEN %lu] first unseen messsage ",
+                            (unsigned long) unseen);
+  io_untagged_response (RESP_NONE, "FLAGS (%s)", mflags);
   /* FIXME:
      - '\*' can be supported if we use the attribute_set userflag()
      - Answered is still not set in the mailbox code.  */
   if (!(select_flags & MU_STREAM_WRITE))
-    util_out (RESP_OK, "[PERMANENTFLAGS ()] No Permanent flags");
+    io_untagged_response (RESP_OK, "[PERMANENTFLAGS ()] No Permanent flags");
   else
-    util_out (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags", pflags);
+    io_untagged_response (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags",
+                          pflags);
 
   return 0;
 }
diff --git a/imap4d/starttls.c b/imap4d/starttls.c
index fbb1e4d..f71d135 100644
--- a/imap4d/starttls.c
+++ b/imap4d/starttls.c
@@ -40,16 +40,16 @@ imap4d_starttls (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   int status;
 
   if (!tls_available || tls_done)
-    return util_finish (command, RESP_BAD, "Invalid command");
+    return io_completion_response (command, RESP_BAD, "Invalid command");
 
   if (imap4d_tokbuf_argc (tok) != 2)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   util_atexit (mu_deinit_tls_libs);
 
-  status = util_finish (command, RESP_OK, "Begin TLS negotiation");
-  util_flush_output ();
-  tls_done = imap4d_init_tls_server ();
+  status = io_completion_response (command, RESP_OK, "Begin TLS negotiation");
+  io_flush ();
+  tls_done = imap4d_init_tls_server () == 0;
 
   if (tls_done)
     {
diff --git a/imap4d/status.c b/imap4d/status.c
index 3622c47..a462829 100644
--- a/imap4d/status.c
+++ b/imap4d/status.c
@@ -79,14 +79,14 @@ imap4d_status (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   int argc = imap4d_tokbuf_argc (tok);
 
   if (argc < 4)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
   
   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
 
   mailbox_name = namespace_getfullpath (name, delim, NULL);
 
   if (!mailbox_name)
-    return util_finish (command, RESP_NO, "Error opening mailbox");
+    return io_completion_response (command, RESP_NO, "Error opening mailbox");
 
   /* We may be opening the current mailbox, so make sure the attributes are
      preserved */
@@ -105,7 +105,8 @@ imap4d_status (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
          if (item[0] == '(')
            {
              if (imap4d_tokbuf_getarg (tok, argc - 1)[0] != ')')
-               return util_finish (command, RESP_BAD, "Invalid arguments");
+               return io_completion_response (command, RESP_BAD, 
+                                              "Invalid arguments");
              argc--;
              i++;
            }
@@ -123,11 +124,11 @@ imap4d_status (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
                }
                  
              if (count++ == 0)
-               util_send ("* STATUS %s (", name);
+               io_sendf ("* STATUS %s (", name);
              else if (!space_sent)
                 {
                   space_sent = 1;
-                 util_send (" ");
+                 io_sendf (" ");
                 }     
 
              if (!fun (smbox))
@@ -136,7 +137,7 @@ imap4d_status (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
 
          
          if (count > 0)
-           util_send (")\n");
+           io_sendf (")\n");
          mu_mailbox_close (smbox);
        }
       mu_mailbox_destroy (&smbox);
@@ -146,13 +147,14 @@ imap4d_status (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   if (status == 0)
     {
       if (count == 0)
-       return util_finish (command, RESP_BAD, "Too few args (empty list)");
+       return io_completion_response (command, RESP_BAD, 
+                                      "Too few args (empty list)");
       else if (err_msg)
-       return util_finish (command, RESP_BAD, err_msg);
-      return util_finish (command, RESP_OK, "Completed");
+       return io_completion_response (command, RESP_BAD, err_msg);
+      return io_completion_response (command, RESP_OK, "Completed");
     }
   
-  return util_finish (command, RESP_NO, "Error opening mailbox");
+  return io_completion_response (command, RESP_NO, "Error opening mailbox");
 }
 
 static int
@@ -160,7 +162,7 @@ status_messages (mu_mailbox_t smbox)
 {
   size_t total = 0;
   mu_mailbox_messages_count (smbox, &total);
-  util_send ("MESSAGES %lu", (unsigned long) total);
+  io_sendf ("MESSAGES %lu", (unsigned long) total);
   return 0;
 }
 
@@ -169,7 +171,7 @@ status_recent (mu_mailbox_t smbox)
 {
   size_t recent = 0;
   mu_mailbox_messages_recent (smbox, &recent);
-  util_send ("RECENT %lu", (unsigned long) recent);
+  io_sendf ("RECENT %lu", (unsigned long) recent);
   return 0;
 }
 
@@ -178,7 +180,7 @@ status_uidnext (mu_mailbox_t smbox)
 {
   size_t uidnext = 1;
   mu_mailbox_uidnext (smbox, &uidnext);
-  util_send ("UIDNEXT %lu", (unsigned long) uidnext);
+  io_sendf ("UIDNEXT %lu", (unsigned long) uidnext);
   return 0;
 }
 
@@ -187,7 +189,7 @@ status_uidvalidity (mu_mailbox_t smbox)
 {
   unsigned long uidvalidity = 0;
   util_uidvalidity (smbox, &uidvalidity);
-  util_send ("UIDVALIDITY %lu", uidvalidity);
+  io_sendf ("UIDVALIDITY %lu", uidvalidity);
   return 0;
 }
 
@@ -214,6 +216,6 @@ status_unseen (mu_mailbox_t smbox)
       if (!mu_attribute_is_read (attr))
        unseen++;
     }
-  util_send ("UNSEEN %lu", (unsigned long) unseen);
+  io_sendf ("UNSEEN %lu", (unsigned long) unseen);
   return 0;
 }
diff --git a/imap4d/store.c b/imap4d/store.c
index c043a3e..e8d592d 100644
--- a/imap4d/store.c
+++ b/imap4d/store.c
@@ -151,13 +151,13 @@ imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char 
**ptext)
          
          if (pclos.ack)
            {
-             util_send ("* %lu FETCH (", (unsigned long) msgno);
+             io_sendf ("* %lu FETCH (", (unsigned long) msgno);
              
              if (isuid)
-               util_send ("UID %lu ", (unsigned long) msgno);
-             util_send ("FLAGS (");
+               io_sendf ("UID %lu ", (unsigned long) msgno);
+             io_sendf ("FLAGS (");
              util_print_flags (attr);
-             util_send ("))\n");
+             io_sendf ("))\n");
            }
          /* Update the flags of uid table.  */
          imap4d_sync_flags (pclos.set[i]);
@@ -178,6 +178,6 @@ imap4d_store (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   char *err_text;
   
   rc = imap4d_store0 (tok, 0, &err_text);
-  return util_finish (command, rc, "%s", err_text);
+  return io_completion_response (command, rc, "%s", err_text);
 }
 
diff --git a/imap4d/subscribe.c b/imap4d/subscribe.c
index b604d04..e694636 100644
--- a/imap4d/subscribe.c
+++ b/imap4d/subscribe.c
@@ -38,7 +38,7 @@ imap4d_subscribe (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   FILE *fp;
 
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
 
@@ -50,7 +50,7 @@ imap4d_subscribe (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
       fputs (name, fp);
       fputs ("\n", fp);
       fclose (fp);
-      return util_finish (command, RESP_OK, "Completed");
+      return io_completion_response (command, RESP_OK, "Completed");
     }
-  return util_finish (command, RESP_NO, "Cannot subscribe");
+  return io_completion_response (command, RESP_NO, "Cannot subscribe");
 }
diff --git a/imap4d/sync.c b/imap4d/sync.c
index 3877a93..c4a5268 100644
--- a/imap4d/sync.c
+++ b/imap4d/sync.c
@@ -102,8 +102,8 @@ notify_flag (size_t msgno, mu_attribute_t oattr)
            add_flag (&abuf, "\\Recent");
          }
       if (*abuf)
-       util_out (RESP_NONE, "%lu FETCH FLAGS (%s)",
-                 (unsigned long) msgno, abuf);
+       io_untagged_response (RESP_NONE, "%lu FETCH FLAGS (%s)",
+                                (unsigned long) msgno, abuf);
       free (abuf);
     }
 }
@@ -125,8 +125,8 @@ notify_deleted (void)
        {
          if (!(uid_table[i].notify))
            {
-             util_out (RESP_NONE, "%lu EXPUNGED",
-                       (unsigned long) uid_table[i].msgno-decr);
+             io_untagged_response (RESP_NONE, "%lu EXPUNGED",
+                                      (unsigned long) uid_table[i].msgno-decr);
              uid_table[i].notify = 1;
              decr++;
            }
@@ -240,8 +240,8 @@ notify (void)
       mu_mailbox_messages_recent (mbox, &recent);
     }
 
-  util_out (RESP_NONE, "%lu EXISTS", (unsigned long) total);
-  util_out (RESP_NONE, "%lu RECENT", (unsigned long) recent);
+  io_untagged_response (RESP_NONE, "%lu EXISTS", (unsigned long) total);
+  io_untagged_response (RESP_NONE, "%lu RECENT", (unsigned long) recent);
 
   if (!reset)
     reset_uids ();
@@ -331,8 +331,8 @@ imap4d_sync (void)
          imap4d_set_observer (mbox);
          free_uids ();
          mailbox_corrupt = 0;
-         util_out (RESP_NONE,
-                   "OK [ALERT] Mailbox modified by another program");
+         io_untagged_response (RESP_NONE,
+                            "OK [ALERT] Mailbox modified by another program");
        }
       notify ();
     }
diff --git a/imap4d/uid.c b/imap4d/uid.c
index 6c9e132..739aff9 100644
--- a/imap4d/uid.c
+++ b/imap4d/uid.c
@@ -32,7 +32,7 @@ imap4d_uid (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
   char *err_text = "Completed";
 
   if (imap4d_tokbuf_argc (tok) < 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   cmd = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
   
@@ -49,5 +49,5 @@ imap4d_uid (struct imap4d_command *command, imap4d_tokbuf_t 
tok)
       err_text = "Uknown uid command";
       rc = RESP_BAD;
     }
-  return util_finish (command, rc, "%s %s", cmd, err_text);
+  return io_completion_response (command, rc, "%s %s", cmd, err_text);
 }
diff --git a/imap4d/unsubscribe.c b/imap4d/unsubscribe.c
index 3ef2ac2..9132095 100644
--- a/imap4d/unsubscribe.c
+++ b/imap4d/unsubscribe.c
@@ -97,7 +97,7 @@ imap4d_unsubscribe (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
   int rc;
   
   if (imap4d_tokbuf_argc (tok) != 3)
-    return util_finish (command, RESP_BAD, "Invalid arguments");
+    return io_completion_response (command, RESP_BAD, "Invalid arguments");
 
   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
 
@@ -128,7 +128,7 @@ imap4d_unsubscribe (struct imap4d_command *command, 
imap4d_tokbuf_t tok)
 
   free (file);
   if (rc)
-    return util_finish (command, RESP_NO, "Cannot unsubscribe");
+    return io_completion_response (command, RESP_NO, "Cannot unsubscribe");
 
-  return util_finish (command, RESP_OK, "Completed");
+  return io_completion_response (command, RESP_OK, "Completed");
 }
diff --git a/imap4d/util.c b/imap4d/util.c
index 11cc49d..a4c5043 100644
--- a/imap4d/util.c
+++ b/imap4d/util.c
@@ -19,11 +19,7 @@
 
 #include "imap4d.h"
 
-static mu_stream_t istream;
-static mu_stream_t ostream;
-
 static int add2set (size_t **, int *, unsigned long);
-static const char *sc2string (int);
 
 /* NOTE: Allocates Memory.  */
 /* Expand: ~ --> /home/user and to ~guest --> /home/guest.  */
@@ -270,155 +266,6 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
 }
 
 int
-util_copy_out (mu_stream_t str, size_t size)
-{
-  return mu_stream_copy (ostream, str, size);
-}
-
-int
-util_send_bytes (const char *buf, size_t size)
-{
-  return mu_stream_write (ostream, buf, size, NULL);
-}
-
-int
-util_send (const char *format, ...)
-{
-  char *buf = NULL;
-  int status = 0;
-  va_list ap;
-
-  va_start (ap, format);
-  vasprintf (&buf, format, ap);
-  va_end (ap);
-  if (!buf)
-      imap4d_bye (ERR_NO_MEM);
-
-#if 0
-  if (imap4d_transcript)
-    mu_diag_output (MU_DIAG_DEBUG, "sent: %s", buf);
-#endif
-
-  status = mu_stream_write (ostream, buf, strlen (buf), NULL);
-  free (buf);
-
-  return status;
-}
-
-/* Send NIL if empty string, change the quoted string to a literal if the
-   string contains: double quotes, CR, LF, and '/'.  CR, LF will be change
-   to spaces.  */
-int
-util_send_qstring (const char *buffer)
-{
-  if (buffer == NULL || *buffer == '\0')
-    return util_send ("NIL");
-  if (strchr (buffer, '"') || strchr (buffer, '\r') || strchr (buffer, '\n')
-      || strchr (buffer, '\\'))
-    {
-      char *s;
-      int ret;
-      char *b = strdup (buffer);
-      while ((s = strchr (b, '\n')) || (s = strchr (b, '\r')))
-       *s = ' ';
-      ret = util_send_literal (b);
-      free (b);
-      return ret;
-    }
-  return util_send ("\"%s\"", buffer);
-}
-
-int
-util_send_literal (const char *buffer)
-{
-  return util_send ("{%lu}\n%s", (unsigned long) strlen (buffer), buffer);
-}
-
-/* Send an unsolicited response.  */
-int
-util_out (int rc, const char *format, ...)
-{
-  char *tempbuf = NULL;
-  char *buf = NULL;
-  int status = 0;
-  va_list ap;
-
-  asprintf (&tempbuf, "* %s%s\n", sc2string (rc), format);
-  va_start (ap, format);
-  vasprintf (&buf, tempbuf, ap);
-  va_end (ap);
-  if (!buf)
-    imap4d_bye (ERR_NO_MEM);
-
-  if (imap4d_transcript)
-    {
-      int len = strcspn (buf, "\n");
-      mu_diag_output (MU_DIAG_DEBUG, "sent: %*.*s", len, len, buf);
-    }
-
-  status = mu_stream_write (ostream, buf, strlen (buf), NULL);
-  free (buf);
-  free (tempbuf);
-  return status;
-}
-
-/* Send the tag response and reset the state.  */
-int
-util_finish (struct imap4d_command *command, int rc, const char *format, ...)
-{
-  size_t size;
-  char *buf = NULL;
-  char *tempbuf = NULL;
-  int new_state;
-  int status = 0;
-  va_list ap;
-  const char *sc = sc2string (rc);
-  
-  va_start (ap, format);
-  vasprintf (&tempbuf, format, ap);
-  va_end (ap);
-  if (!tempbuf)
-    imap4d_bye (ERR_NO_MEM);
-  
-  size = strlen (command->tag) + 1 +
-         strlen (sc) + strlen (command->name) + 1 +
-         strlen (tempbuf) + 1;
-  buf = malloc (size);
-  if (!buf)
-    imap4d_bye (ERR_NO_MEM);
-  strcpy (buf, command->tag);
-  strcat (buf, " ");
-  strcat (buf, sc);
-  strcat (buf, command->name);
-  strcat (buf, " ");
-  strcat (buf, tempbuf);
-  free (tempbuf);
-
-  if (imap4d_transcript)
-    mu_diag_output (MU_DIAG_DEBUG, "sent: %s", buf);
-
-  mu_stream_write (ostream, buf, strlen (buf), NULL);
-  free (buf);
-  mu_stream_write (ostream, "\n", 2, NULL);
-
-  /* Reset the state.  */
-  if (rc == RESP_OK)
-    new_state = command->success;
-  else if (command->failure <= state)
-    new_state = command->failure;
-  else
-    new_state = STATE_NONE;
-
-  if (new_state != STATE_NONE)
-    {
-      util_run_events (state, new_state);
-      state = new_state;
-    }
-  
-  return status;
-}
-
-int
 util_do_command (imap4d_tokbuf_t tok)
 {
   char *tag, *cmd;
@@ -430,13 +277,13 @@ util_do_command (imap4d_tokbuf_t tok)
     {
       nullcommand.name = "";
       nullcommand.tag = (char *) "*";
-      return util_finish (&nullcommand, RESP_BAD, "Null command");
+      return io_completion_response (&nullcommand, RESP_BAD, "Null command");
     }
   else if (argc == 1)
     {
       nullcommand.name = "";
       nullcommand.tag = imap4d_tokbuf_getarg (tok, 0);
-      return util_finish (&nullcommand, RESP_BAD, "Missing command");
+      return io_completion_response (&nullcommand, RESP_BAD, "Missing 
command");
     }
 
   tag = imap4d_tokbuf_getarg (tok, 0);
@@ -447,13 +294,13 @@ util_do_command (imap4d_tokbuf_t tok)
     {
       nullcommand.name = "";
       nullcommand.tag = tag;
-      return util_finish (&nullcommand, RESP_BAD, "Invalid command");
+      return io_completion_response (&nullcommand, RESP_BAD, "Invalid 
command");
     }
 
   command->tag = tag;
 
   if (command->states && (command->states & state) == 0)
-    return util_finish (command, RESP_BAD, "Wrong state");
+    return io_completion_response (command, RESP_BAD, "Wrong state");
 
   return command->func (command, tok);
 }
@@ -472,30 +319,6 @@ util_getcommand (char *cmd, struct imap4d_command 
command_table[])
   return NULL;
 }
 
-/* Status Code to String.  */
-static const char *
-sc2string (int rc)
-{
-  switch (rc)
-    {
-    case RESP_OK:
-      return "OK ";
-
-    case RESP_BAD:
-      return "BAD ";
-
-    case RESP_NO:
-      return "NO ";
-
-    case RESP_BYE:
-      return "BYE ";
-
-    case RESP_PREAUTH:
-      return "PREAUTH ";
-    }
-  return "";
-}
-
 static int
 add2set (size_t ** set, int *n, unsigned long val)
 {
@@ -652,17 +475,17 @@ util_print_flags (mu_attribute_t attr)
     if (flags & _imap4d_attrlist[i].flag)
       {
        if (space)
-         util_send (" ");
+         io_sendf (" ");
        else
          space = 1;
-       util_send (_imap4d_attrlist[i].name);
+       io_sendf (_imap4d_attrlist[i].name);
       }
 
   if (MU_ATTRIBUTE_IS_UNSEEN (flags))
     {
       if (space)
-       util_send (" ");
-      util_send ("\\Recent");
+       io_sendf (" ");
+      io_sendf ("\\Recent");
     }
 }
 
@@ -802,115 +625,6 @@ util_uidvalidity (mu_mailbox_t smbox, unsigned long 
*uidvp)
   return mu_mailbox_uidvalidity (smbox, uidvp);
 }
 
-
-void
-util_setio (FILE *in, FILE *out)
-{
-  mu_stream_t tmp;
-  
-  if (!in)
-    imap4d_bye (ERR_NO_IFILE);
-  if (!out)
-    imap4d_bye (ERR_NO_OFILE);
-
-  if (mu_stdio_stream_create (&tmp, fileno (in), 0))
-    imap4d_bye (ERR_NO_IFILE);
-  mu_stream_set_buffer (tmp, mu_buffer_line, 1024);
-  mu_filter_create (&istream, tmp, "CRLF", MU_FILTER_DECODE,
-                    MU_STREAM_READ | MU_STREAM_AUTOCLOSE);
-  mu_stream_set_buffer (istream, mu_buffer_line, 1024);
-  
-  if (mu_stdio_stream_create (&tmp, fileno (out), 0))
-    imap4d_bye (ERR_NO_OFILE);
-  mu_stream_set_buffer (tmp, mu_buffer_line, 1024);
-  mu_filter_create (&ostream, tmp, "CRLF", MU_FILTER_ENCODE,
-                   MU_STREAM_WRITE | MU_STREAM_AUTOCLOSE);
-  mu_stream_set_buffer (ostream, mu_buffer_line, 1024);
-}
-
-void
-util_get_input (mu_stream_t *pstr)
-{
-  *pstr = istream;
-}
-
-void
-util_get_output (mu_stream_t *pstr)
-{
-  *pstr = ostream;
-}
-
-void
-util_set_input (mu_stream_t str)
-{
-  istream = str;
-}
-
-void
-util_set_output (mu_stream_t str)
-{
-  ostream = str;
-}
-
-/* Wait TIMEOUT seconds for data on the input stream.
-   Returns 0   if no data available
-           1   if some data is available
-          -1  an error occurred */
-int
-util_wait_input (int timeout)
-{
-  int wflags = MU_STREAM_READY_RD;
-  struct timeval tv;
-  int status;
-  
-  tv.tv_sec = timeout;
-  tv.tv_usec = 0;
-  status = mu_stream_wait (istream, &wflags, &tv);
-  if (status)
-    {
-      mu_diag_output (MU_DIAG_ERROR, _("cannot poll input stream: %s"),
-                     mu_strerror(status));
-      return -1;
-    }
-  return wflags & MU_STREAM_READY_RD;
-}
-
-void
-util_flush_output ()
-{
-  mu_stream_flush (ostream);
-}
-
-int
-util_is_master ()
-{
-  return ostream == NULL;
-}
-
-#ifdef WITH_TLS
-int
-imap4d_init_tls_server ()
-{
-  mu_stream_t stream;
-  int rc;
- 
-  rc = mu_tls_server_stream_create (&stream, istream, ostream, 0);
-  if (rc)
-    return 0;
-
-  rc = mu_stream_open (stream);
-  if (rc)
-    {
-      mu_diag_output (MU_DIAG_ERROR, _("Cannot open TLS stream: %s"),
-                     mu_stream_strerror (stream, rc));
-      return 0;
-    }
-
-  istream = ostream = stream;
-  return 1;
-}
-#endif /* WITH_TLS */
-
 static mu_list_t atexit_list;
 
 void
@@ -931,86 +645,11 @@ atexit_run (void *item, void *data)
 void
 util_bye ()
 {
-  int rc = istream != ostream;
-
-  mu_stream_close (istream);
-  mu_stream_destroy (&istream);
-
-  if (rc)
-    {
-      mu_stream_close (ostream);
-      mu_stream_destroy (&ostream);
-    }
+  mu_stream_close (iostream);
+  mu_stream_destroy (&iostream);
   mu_list_do (atexit_list, atexit_run, 0);
 }
 
-struct state_event {
-  int old_state;
-  int new_state;
-  mu_list_action_t *action;
-  void *data;
-};
-
-static mu_list_t event_list;
-
-void
-util_register_event (int old_state, int new_state,
-                    mu_list_action_t *action, void *data)
-{
-  struct state_event *evp = malloc (sizeof (*evp));
-  if (!evp)
-    imap4d_bye (ERR_NO_MEM);
-  evp->old_state = old_state;
-  evp->new_state = new_state;
-  evp->action = action;
-  evp->data = data;
-  if (!event_list)
-    {
-      mu_list_create (&event_list);
-      mu_list_set_destroy_item (event_list, mu_list_free_item);
-    }
-  mu_list_append (event_list, (void*)evp);
-}
-
-void
-util_event_remove (void *id)
-{
-  mu_list_remove (event_list, id);
-}
-
-static int
-event_exec (void *item, void *data)
-{
-  struct state_event *ev = data, *elem = item;
-
-  if (ev->old_state == elem->old_state && ev->new_state == elem->new_state)
-    return elem->action (item, elem->data);
-  return 0;
-}
-
-void
-util_run_events (int old_state, int new_state)
-{
-  if (event_list)
-    {
-      struct state_event ev;
-      mu_iterator_t itr;
-      ev.old_state = old_state;
-      ev.new_state = new_state;
-
-      mu_list_get_iterator (event_list, &itr);
-      for (mu_iterator_first (itr);
-          !mu_iterator_is_done (itr); mu_iterator_next (itr))
-       {
-         struct state_event *p;
-         mu_iterator_current (itr, (void **)&p);
-         if (event_exec (p, &ev))
-           break;
-       }
-      mu_iterator_destroy (&itr);
-    }
-}
-  
 void
 util_chdir (const char *dir)
 {
@@ -1033,351 +672,3 @@ is_atom (const char *s)
   return 1;
 }
      
-
-static size_t
-unquote (char *line, size_t len)
-{
-  char *prev = NULL;
-  size_t rlen = len;
-  char *p;
-  int off = 0;
-  while ((p = memchr (line + off, '\\', len - off)))
-    {
-      if (p[1] == '\\' || p[1] == '"')
-       {
-         if (prev)
-           {
-             memmove (prev, line, p - line);
-             prev += p - line;
-           }
-         else
-           prev = p;
-         off = p[1] == '\\';
-         rlen--;
-         len -= p - line + 1;
-         line = p + 1;
-       }
-    }
-  if (prev)
-    memmove (prev, line, len);
-  return rlen;
-}
-
-struct imap4d_tokbuf {
-  char *buffer;
-  size_t size;
-  size_t level;
-  int argc;
-  int argmax;
-  size_t *argp;
-};
-
-struct imap4d_tokbuf *
-imap4d_tokbuf_init ()
-{
-  struct imap4d_tokbuf *tok = malloc (sizeof (tok[0]));
-  if (!tok)
-    imap4d_bye (ERR_NO_MEM);
-  memset (tok, 0, sizeof (*tok));
-  return tok;
-}
-
-void
-imap4d_tokbuf_destroy (struct imap4d_tokbuf **ptok)
-{
-  struct imap4d_tokbuf *tok = *ptok;
-  free (tok->buffer);
-  free (tok->argp);
-  free (tok);
-  *ptok = NULL;
-}
-
-int
-imap4d_tokbuf_argc (struct imap4d_tokbuf *tok)
-{
-  return tok->argc;
-}
-
-char *
-imap4d_tokbuf_getarg (struct imap4d_tokbuf *tok, int n)
-{
-  if (n < tok->argc)
-    return tok->buffer + tok->argp[n];
-  return NULL;
-}
-
-static void
-imap4d_tokbuf_unquote (struct imap4d_tokbuf *tok, size_t *poff, size_t *plen)
-{
-  char *buf = tok->buffer + *poff;
-  if (buf[0] == '"' && buf[*plen - 1] == '"')
-    {
-      ++*poff;
-      *plen = unquote (buf + 1, *plen - 1);
-    }
-}
-
-static void
-imap4d_tokbuf_expand (struct imap4d_tokbuf *tok, size_t size)
-{
-  if (tok->size - tok->level < size)          
-    {                                          
-      tok->size = tok->level + size;
-      tok->buffer = realloc (tok->buffer, tok->size);
-      if (!tok->buffer)                                
-       imap4d_bye (ERR_NO_MEM);
-    }
-}
-
-#define ISDELIM(c) (strchr ("()", (c)) != NULL)
-
-int
-util_isdelim (const char *str)
-{
-  return str[1] == 0 && ISDELIM (str[0]);
-}
-
-static size_t
-insert_nul (struct imap4d_tokbuf *tok, size_t off)
-{
-  imap4d_tokbuf_expand (tok, 1);
-  if (off < tok->level)
-    {
-      memmove (tok->buffer + off + 1, tok->buffer + off, tok->level - off);
-      tok->level++;
-    }
-  tok->buffer[off] = 0;
-  return off + 1;
-}
-
-static size_t
-gettok (struct imap4d_tokbuf *tok, size_t off)
-{
-  char *buf = tok->buffer;
-  
-  while (off < tok->level && mu_isblank (buf[off]))
-    off++;
-
-  if (tok->argc == tok->argmax)
-    {
-      if (tok->argmax == 0)
-       tok->argmax = 16;
-      else
-       tok->argmax *= 2;
-      tok->argp = realloc (tok->argp, tok->argmax * sizeof (tok->argp[0]));
-      if (!tok->argp)
-       imap4d_bye (ERR_NO_MEM);
-    }
-  
-  if (buf[off] == '"')
-    {
-      char *start = buf + off + 1;
-      char *p = NULL;
-      
-      while (*start && (p = strchr (start, '"')))
-       {
-         if (p == start || p[-1] != '\\')
-           break;
-         start = p + 1;
-       }
-
-      if (p)
-       {
-         size_t len;
-         off++;
-         len  = unquote (buf + off, p - (buf + off));
-         buf[off + len] = 0;
-         tok->argp[tok->argc++] = off;
-         return p - buf + 1;
-       }
-    }
-
-  tok->argp[tok->argc++] = off;
-  if (ISDELIM (buf[off]))
-    return insert_nul (tok, off + 1);
-
-  while (off < tok->level && !mu_isblank (buf[off]))
-    {
-      if (ISDELIM (buf[off]))
-       return insert_nul (tok, off);
-      off++;
-    }
-  buf[off++] = 0;
-  
-  return off;
-}
-
-static void
-imap4d_tokbuf_tokenize (struct imap4d_tokbuf *tok, size_t off)
-{
-  while (off < tok->level)
-    off = gettok (tok, off);
-}
-
-static void
-check_input_err (int rc, size_t sz)
-{
-  if (rc)
-    {
-      const char *p = mu_stream_strerror (istream, rc);
-      if (!p)
-       p = mu_strerror (rc);
-      
-      mu_diag_output (MU_DIAG_INFO,
-                     _("error reading from input file: %s"), p);
-      imap4d_bye (ERR_NO_IFILE);
-    }
-  else if (sz == 0)
-    {
-      mu_diag_output (MU_DIAG_INFO, _("unexpected eof on input"));
-      imap4d_bye (ERR_NO_IFILE);
-    }
-}
-
-static size_t
-imap4d_tokbuf_getline (struct imap4d_tokbuf *tok)
-{
-  char buffer[512];
-  size_t level = tok->level;
-  
-  do
-    {
-      size_t len;
-      int rc;
-      
-      rc = mu_stream_readline (istream, buffer, sizeof (buffer), &len);
-      check_input_err (rc, len);
-      imap4d_tokbuf_expand (tok, len);
-      
-      memcpy (tok->buffer + tok->level, buffer, len);
-      tok->level += len;
-    }
-  while (tok->level && tok->buffer[tok->level - 1] != '\n');
-  tok->buffer[--tok->level] = 0;
-  if (tok->buffer[tok->level - 1] == '\r')
-    tok->buffer[--tok->level] = 0;
-  return level;
-}
-
-void
-imap4d_readline (struct imap4d_tokbuf *tok)
-{
-  int transcript = imap4d_transcript;
-  tok->argc = 0;
-  tok->level = 0;
-  for (;;)
-    {
-      char *last_arg;
-      size_t off = imap4d_tokbuf_getline (tok);
-      if (transcript)
-        {
-          int len;
-          char *p = mu_strcasestr (tok->buffer, "LOGIN");
-          if (p && p > tok->buffer && mu_isblank (p[-1]))
-            {
-             char *q = mu_str_skip_class (p + 5, MU_CTYPE_SPACE);
-             q = mu_str_skip_class_comp (q, MU_CTYPE_SPACE);
-              len = q - tok->buffer; 
-              mu_diag_output (MU_DIAG_DEBUG,
-                             "recv: %*.*s {censored}", len, len,
-                              tok->buffer);
-             }
-           else
-             {
-               len = strcspn (tok->buffer, "\r\n");
-               mu_diag_output (MU_DIAG_DEBUG, "recv: %*.*s", 
-                               len, len, tok->buffer);
-             }
-        }
-      imap4d_tokbuf_tokenize (tok, off);
-      if (tok->argc == 0)
-        break;  
-      last_arg = tok->buffer + tok->argp[tok->argc - 1];
-      if (last_arg[0] == '{' && last_arg[strlen(last_arg)-1] == '}')
-       {
-         int rc;
-         unsigned long number;
-         char *sp = NULL;
-         char *buf;
-         size_t len;
-         
-          if (transcript)
-            mu_diag_output (MU_DIAG_DEBUG, "(literal follows)");
-          transcript = 0;
-         number = strtoul (last_arg + 1, &sp, 10);
-         /* Client can ask for non-synchronised literal,
-            if a '+' is appended to the octet count. */
-         if (*sp == '}')
-           util_send ("+ GO AHEAD\n");
-         else if (*sp != '+')
-           break;
-         imap4d_tokbuf_expand (tok, number + 1);
-         off = tok->level;
-         buf = tok->buffer + off;
-          len = 0;
-          while (len < number)
-            {
-               size_t sz;
-              rc = mu_stream_read (istream, buf + len, number - len, &sz);
-               if (rc || sz == 0)
-                 break;
-               len += sz;
-            }
-         check_input_err (rc, len);
-         imap4d_tokbuf_unquote (tok, &off, &len);
-         tok->level += len;
-         tok->buffer[tok->level++] = 0;
-         tok->argp[tok->argc - 1] = off;
-       }
-      else
-       break;
-    }
-}  
-
-struct imap4d_tokbuf *
-imap4d_tokbuf_from_string (char *str)
-{
-  struct imap4d_tokbuf *tok = imap4d_tokbuf_init ();
-  tok->buffer = strdup (str);
-  if (!tok->buffer)
-    imap4d_bye (ERR_NO_MEM);
-  tok->level = strlen (str);
-  tok->size = tok->level + 1;
-  imap4d_tokbuf_tokenize (tok, 0);
-  return tok;
-}
-
-int
-util_trim_nl (char *s, size_t len)
-{
-  if (s && len > 0 && s[len - 1] == '\n')
-    s[--len] = 0;
-  if (s && len > 0 && s[len - 1] == '\r')
-    s[--len] = 0;
-  return len;
-}
-
-int
-imap4d_getline (char **pbuf, size_t *psize, size_t *pnbytes)
-{
-  size_t len;
-  int rc = mu_stream_getline (istream, pbuf, psize, &len);
-  if (rc == 0)
-    {
-      char *s = *pbuf;
-
-      if (len == 0)
-        {
-         if (imap4d_transcript)
-            mu_diag_output (MU_DIAG_DEBUG, "got EOF");
-          imap4d_bye (ERR_NO_IFILE);
-          /*FIXME rc = ECONNABORTED;*/
-        }
-      len = util_trim_nl (s, len);
-      if (imap4d_transcript)
-       mu_diag_output (MU_DIAG_DEBUG, "recv: %s", s);
-      if (pnbytes)
-       *pnbytes = len;
-    }
-  return rc;
-}
diff --git a/include/mailutils/gsasl.h b/include/mailutils/gsasl.h
index 583b356..be5f2bb 100644
--- a/include/mailutils/gsasl.h
+++ b/include/mailutils/gsasl.h
@@ -36,8 +36,10 @@ extern struct mu_gsasl_module_data mu_gsasl_module_data;
 #ifdef WITH_GSASL
 #include <gsasl.h>
 
-int mu_gsasl_stream_create (mu_stream_t *stream, mu_stream_t transport,
-                           Gsasl_session *ctx, int flags);
+int gsasl_encoder_stream (mu_stream_t *pstr, mu_stream_t transport,
+                         Gsasl_session *ctx, int flags);
+int gsasl_decoder_stream (mu_stream_t *pstr, mu_stream_t transport,
+                         Gsasl_session *ctx, int flags);
 
 #endif
 
diff --git a/include/mailutils/sys/Makefile.am 
b/include/mailutils/sys/Makefile.am
index ce37112..bc813e9 100644
--- a/include/mailutils/sys/Makefile.am
+++ b/include/mailutils/sys/Makefile.am
@@ -22,6 +22,7 @@ sysinclude_HEADERS = \
  dbgstream.h\
  file_stream.h\
  filter.h\
+ gsasl-stream.h\
  header_stream.h\
  header.h\
  iostream.h\
diff --git a/include/mailutils/sys/xscript-stream.h 
b/include/mailutils/sys/gsasl-stream.h
similarity index 78%
copy from include/mailutils/sys/xscript-stream.h
copy to include/mailutils/sys/gsasl-stream.h
index c6b4538..c931767 100644
--- a/include/mailutils/sys/xscript-stream.h
+++ b/include/mailutils/sys/gsasl-stream.h
@@ -14,20 +14,25 @@
    You should have received a copy of the GNU Lesser General Public License
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
-#ifndef _MAILUTILS_SYS_XSCRIPT_STREAM_H
-# define _MAILUTILS_SYS_XSCRIPT_STREAM_H
+#ifndef _MAILUTILS_SYS_TLS_STREAM_H
+# define _MAILUTILS_SYS_GSASL_STREAM_H
 
 # include <mailutils/types.h>
 # include <mailutils/stream.h>
 # include <mailutils/sys/stream.h>
 
-struct _mu_xscript_stream
+struct _mu_gsasl_filter
+{
+  Gsasl_session *sess_ctx; /* Context */
+  int gsasl_err;        /* Last Gsasl error code */
+  char *bufptr;
+  size_t bufsize;
+};
+
+struct _mu_gsasl_stream
 {
   struct _mu_stream stream;
   mu_stream_t transport;
-  mu_stream_t logstr;
-  int flags;
-  char *prefix[2];
 };
 
 #endif
diff --git a/libmu_auth/Makefile.am b/libmu_auth/Makefile.am
index 0f97e55..2533ffb 100644
--- a/libmu_auth/Makefile.am
+++ b/libmu_auth/Makefile.am
@@ -26,8 +26,6 @@ lib_LTLIBRARIES = libmu_auth.la
 
 libmu_auth_la_SOURCES = \
  gsasl.c\
- lbuf.c\
- lbuf.h\
  ldap.c\
  pam.c\
  radius.c\
diff --git a/libmu_auth/gsasl.c b/libmu_auth/gsasl.c
index bdaa117..d03f3e2 100644
--- a/libmu_auth/gsasl.c
+++ b/libmu_auth/gsasl.c
@@ -33,9 +33,10 @@
 #include <mailutils/nls.h>
 #include <mailutils/stream.h>
 #include <mailutils/gsasl.h>
+#include <mailutils/sys/gsasl-stream.h>
+#include <mailutils/filter.h>
 
 #include <gsasl.h>
-#include <lbuf.h>
 
 struct mu_gsasl_module_data mu_gsasl_module_data = {
     SITE_CRAM_MD5_PWD
@@ -49,235 +50,172 @@ mu_gsasl_module_init (enum mu_gocs_op op, void *data)
   return 0;
 }
 
-struct _gsasl_stream {
-  Gsasl_session *sess_ctx; /* Context */
-  int last_err;        /* Last Gsasl error code */
-  
-  mu_stream_t stream;     /* I/O stream */
-  struct _line_buffer *lb; 
-};
-
-static void
-_gsasl_destroy (mu_stream_t stream)
-{
-  int flags;
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-  mu_stream_get_flags (stream, &flags);
-  if (!(flags & MU_STREAM_NO_CLOSE))
-    mu_stream_destroy (&s->stream, mu_stream_get_owner (s->stream));
-  _auth_lb_destroy (&s->lb);
-}
-
-static int
-_gsasl_readline (mu_stream_t stream, char *optr, size_t osize,
-                off_t offset, size_t *nbytes)
+
+static enum mu_filter_result
+_gsasl_encoder (void *xdata,
+               enum mu_filter_command cmd,
+               struct mu_filter_io *iobuf)
 {
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-  int rc;
-  size_t len, sz;
-  char *bufp = NULL;
+  struct _mu_gsasl_filter *flt = xdata;
   
-  if (_auth_lb_level (s->lb))
+  switch (cmd)
     {
-      len = _auth_lb_readline (s->lb, optr, osize-1);
-      optr[len] = 0;
-      if (nbytes)
-       *nbytes = len;
-      return 0;
+    case mu_filter_init:
+      flt->bufptr = NULL;
+      flt->bufsize = 0;
+      flt->gsasl_err = 0;
+      return mu_filter_ok;
+      
+    case mu_filter_done:
+      if (flt->bufptr)
+       free (flt->bufptr);
+      free (flt);
+      return mu_filter_ok;
+      
+    default:
+      break;
     }
 
-  do
+  if (flt->bufptr == NULL)
     {
-      char c;
-      size_t sz;
-      int status;
-      
-      status = mu_stream_sequential_read (s->stream, &c, 1, &sz);
-      if (status == EINTR)
-       continue;
-      else if (status)
+      int status = gsasl_encode (flt->sess_ctx, iobuf->input, iobuf->isize,
+                                &flt->bufptr, &flt->bufsize);
+      /* FIXME: Can it require more input? */
+      if (status)
        {
-         free (bufp);
-         return status;
+         flt->gsasl_err = status;
+         return mu_filter_falure;
        }
-      rc = _auth_lb_grow (s->lb, &c, sz);
-      if (rc)
-       return rc;
-      
-      rc = gsasl_decode (s->sess_ctx,
-                        _auth_lb_data (s->lb),
-                        _auth_lb_level (s->lb),
-                        &bufp, &len);
     }
-  while (rc == GSASL_NEEDS_MORE);
+    
+  iobuf->osize = flt->bufsize;
 
-  if (rc != GSASL_OK)
-    {
-      s->last_err = rc;
-      free (bufp);
-      return EIO;
-    }
-      
-  sz = len > osize ? osize : len;
-  
-  if (len > osize)
-    {
-      memcpy (optr, bufp, osize);
-      _auth_lb_drop (s->lb);
-      _auth_lb_grow (s->lb, bufp + osize, len - osize);
-      len = osize;
-    }
-  else
-    {
-      _auth_lb_drop (s->lb);
-      memcpy (optr, bufp, len);
-    }
+  if (flt->bufsize > iobuf->osize)
+    return mu_filter_moreoutput;
 
-  if (len < osize)
-    optr[len] = 0;
-  
-  if (nbytes)
-    *nbytes = len;
-  
-  free (bufp);
+  memcpy (iobuf->output, flt->bufptr, flt->bufsize);
 
-  return 0;
+  free (flt->bufptr);
+  flt->bufptr = NULL;
+  flt->bufsize = 0;
+  
+  return mu_filter_ok;
 }
-
-int
-write_chunk (void *data, char *start, char *end)
+       
+       
+static enum mu_filter_result
+_gsasl_decoder (void *xdata,
+               enum mu_filter_command cmd,
+               struct mu_filter_io *iobuf)
 {
-  struct _gsasl_stream *s = data;
-  size_t chunk_size = end - start + 1;
-  size_t len = 0;
-  char *buf = NULL;
+  struct _mu_gsasl_filter *flt = xdata;
   int status;
-    
-  gsasl_encode (s->sess_ctx, start, chunk_size, &buf, &len);
-
-  status = mu_stream_sequential_write (s->stream, buf, len);
   
-  free (buf);
+  switch (cmd)
+    {
+    case mu_filter_init:
+      flt->bufptr = NULL;
+      flt->bufsize = 0;
+      flt->gsasl_err = 0;
+      return mu_filter_ok;
+      
+    case mu_filter_done:
+      if (flt->bufptr)
+       free (flt->bufptr);
+      free (flt);
+      return mu_filter_ok;
+      
+    default:
+      break;
+    }
 
-  return status;
-}
+  if (flt->bufptr == NULL)
+    {
+      status = gsasl_decode (flt->sess_ctx, iobuf->input, iobuf->isize,
+                            &flt->bufptr, &flt->bufsize);
+      switch (status)
+       {
+       case GSASL_OK:
+         break;
+         
+       case GSASL_NEEDS_MORE:
+         iobuf->isize++;
+         return mu_filter_moreinput;
+         
+       default:
+         flt->gsasl_err = status;
+         return mu_filter_falure;
+       }
+    }
 
+  iobuf->osize = flt->bufsize;
 
-static int
-_gsasl_write (mu_stream_t stream, const char *iptr, size_t isize,
-             off_t offset, size_t *nbytes)
-{
-  int rc;
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
+  if (flt->bufsize > iobuf->osize)
+    return mu_filter_moreoutput;
   
-  rc = _auth_lb_grow (s->lb, iptr, isize);
-  if (rc)
-    return rc;
-
-  return _auth_lb_writelines (s->lb, iptr, isize, offset,
-                             write_chunk, s, nbytes);      
-}
-
-static int
-_gsasl_flush (mu_stream_t stream)
-{
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-  return mu_stream_flush (s->stream);
-}
+  memcpy (iobuf->output, flt->bufptr, flt->bufsize);
 
-static int
-_gsasl_close (mu_stream_t stream)
-{
-  int flags;
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-
-  mu_stream_get_flags (stream, &flags);
-  if (!(flags & MU_STREAM_NO_CLOSE))
-    mu_stream_close (s->stream);
-  return 0;
-}
-
-static int
-_gsasl_open (mu_stream_t stream)
-{
-  /* Nothing to do */
-  return 0;
-}
-
-int
-_gsasl_strerror (mu_stream_t stream, const char **pstr)
-{
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-  *pstr = gsasl_strerror (s->last_err);
-  return 0;
+  free (flt->bufptr);
+  flt->bufptr = NULL;
+  flt->bufsize = 0;
+  
+  return mu_filter_ok;
 }
-
+  
 int
-_gsasl_get_transport2 (mu_stream_t stream, mu_transport_t *pt, mu_transport_t 
*pt2)
+gsasl_encoder_stream (mu_stream_t *pstr, mu_stream_t transport,
+                     Gsasl_session *ctx, int flags)
 {
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-  *pt2 = NULL; /* FIXME 1 */
-  *pt = (mu_transport_t) s->stream;
-  return 0;
+  int rc;
+  struct _mu_gsasl_filter *flt = calloc (1, sizeof (*flt));
+  flt->sess_ctx = ctx;
+  rc = mu_filter_stream_create (pstr, transport, 
+                               MU_FILTER_ENCODE, 
+                               _gsasl_encoder,
+                               flt, flags);
+  if (rc == 0)
+    mu_stream_set_buffer (*pstr, mu_buffer_line, 1024);
+  return rc;
 }
 
 int
-_gsasl_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
+gsasl_decoder_stream (mu_stream_t *pstr, mu_stream_t transport,
+                     Gsasl_session *ctx, int flags)
 {
-  int flags;
-  struct _gsasl_stream *s = mu_stream_get_owner (stream);
-
-  mu_stream_get_flags (stream, &flags);
-  if (((*pflags & MU_STREAM_READY_RD) && !(flags & MU_STREAM_READ))
-      || ((*pflags & MU_STREAM_READY_WR) && !(flags & MU_STREAM_WRITE)))
-    return EINVAL; 
-  return mu_stream_wait (s->stream, pflags, tvp);
+  int rc;
+  struct _mu_gsasl_filter *flt = calloc (1, sizeof (*flt));
+  flt->sess_ctx = ctx;
+  rc = mu_filter_stream_create (pstr, transport, 
+                               MU_FILTER_DECODE, 
+                               _gsasl_decoder,
+                               flt, flags);
+  if (rc == 0)
+    mu_stream_set_buffer (*pstr, mu_buffer_line, 1024);
+  return rc;
 }
 
 int
 mu_gsasl_stream_create (mu_stream_t *stream, mu_stream_t transport,
                        Gsasl_session *ctx, int flags)
 {
-  struct _gsasl_stream *s;
   int rc;
-    
+  mu_stream_t in, out;
+  
   if (stream == NULL)
     return MU_ERR_OUT_PTR_NULL;
-
-  if ((flags & ~(MU_STREAM_READ|MU_STREAM_WRITE))
-      || (flags & (MU_STREAM_READ|MU_STREAM_WRITE)) ==
-          (MU_STREAM_READ|MU_STREAM_WRITE))
-    return EINVAL;
-  
-  s = calloc (1, sizeof (*s));
-  if (s == NULL)
-    return ENOMEM;
-
-  s->stream = transport;
-  s->sess_ctx = ctx;
-  
-  rc = mu_stream_create (stream, flags, s);
+  rc = gsasl_encoder_stream (&in, transport, ctx, MU_STREAM_READ);
+  if (rc)
+    return rc;
+  rc = gsasl_encoder_stream (&out, transport, ctx, MU_STREAM_WRITE);
   if (rc)
     {
-      free (s);
+      mu_stream_destroy (&in);
       return rc;
     }
-
-  mu_stream_set_open (*stream, _gsasl_open, s);
-  mu_stream_set_close (*stream, _gsasl_close, s);
-  mu_stream_set_flush (*stream, _gsasl_flush, s);
-  mu_stream_set_destroy (*stream, _gsasl_destroy, s);
-  mu_stream_set_strerror (*stream, _gsasl_strerror, s);
-  mu_stream_set_wait (*stream, _gsasl_wait, s);
-  mu_stream_set_get_transport2 (*stream, _gsasl_get_transport2, s);
-  if (flags & MU_STREAM_READ)
-    mu_stream_set_readline (*stream, _gsasl_readline, s);
-  else
-    mu_stream_set_write (*stream, _gsasl_write, s);
-
-  _auth_lb_create (&s->lb);
-  
-  return 0;
+  rc = mu_iostream_create (stream, in, out);
+  mu_stream_unref (in);
+  mu_stream_unref (out);
+  return rc;
 }
   
 #endif
diff --git a/libmu_auth/lbuf.c b/libmu_auth/lbuf.c
deleted file mode 100644
index ac80284..0000000
--- a/libmu_auth/lbuf.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2003, 2007, 2010 Free Software Foundation, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 3 of the License, or (at your option) any later version.
-
-   This library 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General
-   Public License along with this library; if not, write to the
-   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301 USA */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <lbuf.h>
-
-struct _line_buffer {
-  char *buffer;        /* Line buffer */
-  size_t size;         /* Allocated size */
-  size_t level;        /* Current filling level */
-};
-
-int
-_auth_lb_create (struct _line_buffer **s)
-{
-  *s = malloc (sizeof (**s));
-  if (!*s)
-    return ENOMEM;
-  (*s)->buffer = NULL;
-  (*s)->size = 0;
-  (*s)->level = 0;
-  return 0;
-}
-
-void
-_auth_lb_destroy (struct _line_buffer **s)
-{
-  if (s && *s)
-    {
-      free ((*s)->buffer);
-      free (*s);
-      *s = NULL;
-    }
-}
-
-void
-_auth_lb_drop (struct _line_buffer *s)
-{
-  s->level = 0;
-}
-
-int
-_auth_lb_grow (struct _line_buffer *s, const char *ptr, size_t size)
-{
-  if (!s->buffer)
-    {
-      s->buffer = malloc (size);
-      s->size = size;
-      s->level = 0;
-    }
-  else if (s->size - s->level < size)
-    {
-      size_t newsize = s->size + size;
-      s->buffer = realloc (s->buffer, newsize);
-      if (s->buffer)
-       s->size = newsize;
-    }
-
-  if (!s->buffer)
-    return ENOMEM;
-  
-  memcpy (s->buffer + s->level, ptr, size);
-  s->level += size;
-  return 0;
-}
-
-int
-_auth_lb_read (struct _line_buffer *s, char *optr, size_t osize)
-{
-  int len;
-
-  len = s->level > osize ? osize : s->level;
-  memcpy (optr, s->buffer, len);
-  if (s->level > len)
-    {
-      memmove (s->buffer, s->buffer + len, s->level - len);
-      s->level -= len;
-    }
-  else if (s->level == len)
-    s->level = 0;
-    
-  return len;
-}
-
-int
-_auth_lb_readline (struct _line_buffer *s, char *ptr, size_t size)
-{
-  char *p = strchr (s->buffer, '\n');
-
-  if (p && p - s->buffer + 1 < size)
-    size = p - s->buffer + 1;
-  return _auth_lb_read (s, ptr, size);
-}
-
-int
-_auth_lb_writelines (struct _line_buffer *s, const char *iptr, size_t isize,
-                    off_t offset,
-                    int (*wr) (void *data, char *start, char *end),
-                    void *data,
-                    size_t *nbytes)
-{
-  if (s->level > 2)
-    {
-      char *start, *end;
-      
-      for (start = s->buffer,
-                  end = memchr (start, '\n', s->buffer + s->level - start);
-          end && end < s->buffer + s->level;
-          start = end + 1,
-                  end = memchr (start, '\n', s->buffer + s->level - start))
-       if (end[-1] == '\r')
-         {
-           int rc = wr (data, start, end);
-           if (rc)
-             return rc;
-         }
-
-      if (start > s->buffer)
-       {
-         if (start < s->buffer + s->level)
-           {
-             int rest = s->buffer + s->level - start;
-             memmove (s->buffer, start, rest);
-             s->level = rest;
-           }
-         else 
-           s->level = 0;
-       }
-    }
-
-  if (nbytes)
-    *nbytes = isize;
-  return 0;
-}
-
-int
-_auth_lb_level (struct _line_buffer *s)
-{
-  return s->level;
-}
-
-char *
-_auth_lb_data (struct _line_buffer *s)
-{
-  return s->buffer;
-}
diff --git a/libmu_auth/lbuf.h b/libmu_auth/lbuf.h
deleted file mode 100644
index 2b7c060..0000000
--- a/libmu_auth/lbuf.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2003, 2005, 2007, 2010 Free Software Foundation, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 3 of the License, or (at your option) any later version.
-
-   This library 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General
-   Public License along with this library; if not, write to the
-   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301 USA */
-
-#include <mailutils/types.h>
-
-struct _line_buffer;
-
-int _auth_lb_create (struct _line_buffer **s);
-void _auth_lb_destroy (struct _line_buffer **s);
-void _auth_lb_drop (struct _line_buffer *s);
-
-int _auth_lb_grow (struct _line_buffer *s, const char *ptr, size_t size);
-int _auth_lb_read (struct _line_buffer *s, char *ptr, size_t size);
-int _auth_lb_readline (struct _line_buffer *s, char *ptr, size_t size);
-int _auth_lb_writelines (struct _line_buffer *s, const char *iptr,
-                        size_t isize, off_t offset,
-                        int (*wr) (void *data, char *start, char *end),
-                        void *data, size_t *nbytes);
-int _auth_lb_level (struct _line_buffer *s);
-char *_auth_lb_data (struct _line_buffer *s);
-
-
diff --git a/mailbox/fltstream.c b/mailbox/fltstream.c
index 0f101e7..38a114f 100644
--- a/mailbox/fltstream.c
+++ b/mailbox/fltstream.c
@@ -313,7 +313,7 @@ filter_wr_flush (mu_stream_t stream)
 {
   struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
   int rc = filter_write_internal (stream, mu_filter_lastbuf, NULL, 0, NULL);
-  if (rc)
+  if (rc == 0)
     rc = mu_stream_flush (fs->transport);
   return rc;
 }
@@ -405,6 +405,14 @@ filter_write_through (struct _mu_stream *stream,
   return mu_stream_write (fs->transport, buf, bufsize, pnwrite);
 }
 
+static int
+filter_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
+{
+  struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+  /* FIXME: Take into account internal buffer state. */
+  return mu_stream_wait (fs->transport, pflags, tvp);
+}
+
 
 int
 mu_filter_stream_create (mu_stream_t *pflt,
@@ -457,6 +465,7 @@ mu_filter_stream_create (mu_stream_t *pflt,
   if (flags & MU_STREAM_SEEK)
     fs->stream.seek = filter_seek;
   fs->stream.ctl = filter_ctl;
+  fs->stream.wait = filter_wait;
   fs->stream.error_string = filter_error_string;
   fs->stream.flags = flags;
 
diff --git a/mailbox/xscript-stream.c b/mailbox/xscript-stream.c
index 34ad523..64a5734 100644
--- a/mailbox/xscript-stream.c
+++ b/mailbox/xscript-stream.c
@@ -176,7 +176,6 @@ _xscript_ctl (struct _mu_stream *str, int op, void *arg)
 {
   struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str;
   mu_transport_t *ptrans;
-  mu_stream_t strtab[2];
   
   switch (op)
     {


hooks/post-receive
-- 
GNU Mailutils



reply via email to

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