commit 8b3bb9ade881d203b54f7495feb2c9530dcbf5be
Author: Oswald Buddenhagen <o...@kde.org>
Date:   Sun Mar 13 11:34:04 2011 +0100

    make imap_exec() result reporting callback-based
    
    this makes the IMAP command submission interface asynchronous.
    
    the imap context is now refcounted, so it can be kept alive after
    imap_cancel_store() is called from a callback, so it can hold a
    cancelation indicator which can be queried by loops (which must hold a
    reference, obviously).
    
    as a "side effect", properly sequence commands after CREATE resulting
    from [TRYCREATE].

 src/drv_imap.c |  917 +++++++++++++++++++++++++++++++++++-------------
 1 files changed, 677 insertions(+), 240 deletions(-)

diff --git a/src/drv_imap.c b/src/drv_imap.c
index 92777b7..be083a5 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -121,12 +121,22 @@ typedef struct imap_store {
        list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
        message_t **msgapp; /* FETCH results */
        unsigned caps; /* CAPABILITY results */
+       int ref_count; /* for ordered destruction */
+       int store_canceled; /* context is invalid, only ref_count keeps it 
alive */
        /* command queue */
        int nexttag, num_in_progress, literal_pending;
        struct imap_cmd *in_progress, **in_progress_append;
 #if HAVE_LIBSSL
        SSL_CTX *SSLContext;
 #endif
+
+       /* Used during sequential operations like connect */
+       int preauth;
+       union {
+               void (*imap_open)( store_t *srv, void *aux );
+       } callbacks;
+       void *callback_aux;
+
        buffer_t buf; /* this is BIG, so put it last */
 } imap_store_t;
 
@@ -138,7 +148,6 @@ struct imap_cmd {
        struct {
                int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const 
char *prompt );
                void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int 
response );
-               void *aux;
                char *data;
                int data_len;
                int uid; /* to identify fetch responses */
@@ -149,6 +158,36 @@ struct imap_cmd {
        } param;
 };
 
+struct imap_cmd_simple {
+       struct imap_cmd gen;
+       void (*callback)( int sts, void *aux );
+       void *callback_aux;
+};
+
+struct imap_cmd_fetch_msg {
+       struct imap_cmd_simple gen;
+       msg_data_t *msg_data;
+};
+
+struct imap_cmd_out_uid {
+       struct imap_cmd gen;
+       void (*callback)( int sts, int uid, void *aux );
+       void *callback_aux;
+       int out_uid;
+};
+
+struct imap_cmd_refcounted_state {
+       void (*callback)( int sts, void *aux );
+       void *callback_aux;
+       int ref_count;
+       int ret_val;
+};
+
+struct imap_cmd_refcounted {
+       struct imap_cmd gen;
+       struct imap_cmd_refcounted_state *state;
+};
+
 #define CAP(cap) (ctx->caps & (1 << (cap)))
 
 enum CAPABILITY {
@@ -173,11 +212,12 @@ static const char *cap_list[] = {
 #endif
 };
 
-#define RESP_OK    0
-#define RESP_NO    1
-#define RESP_BAD   2
+#define RESP_OK       0
+#define RESP_NO       1
+#define RESP_BAD      2
+#define RESP_CANCEL   3
 
-static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
+static void get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
 
 
 static const char *Flags[] = {
@@ -487,14 +527,31 @@ buffer_gets( buffer_t * b, char **s )
        /* not reached */
 }
 
+static void
+deref_store( imap_store_t *ctx )
+{
+       if (!--ctx->ref_count)
+               free( ctx );
+}
+
 static struct imap_cmd *
-new_imap_cmd( void )
+new_imap_cmd( int size )
 {
-       struct imap_cmd *cmd = nfmalloc( sizeof(*cmd) );
+       struct imap_cmd *cmd = nfmalloc( size );
        memset( &cmd->param, 0, sizeof(cmd->param) );
        return cmd;
 }
 
+#define INIT_IMAP_CMD(type, cmdp, cb, aux) \
+       cmdp = (struct type *)new_imap_cmd( sizeof(*cmdp) ); \
+       cmdp->callback = cb; \
+       cmdp->callback_aux = aux;
+
+#define INIT_IMAP_CMD_X(type, cmdp, cb, aux) \
+       cmdp = (struct type *)new_imap_cmd( sizeof(*cmdp) ); \
+       cmdp->gen.callback = cb; \
+       cmdp->gen.callback_aux = aux;
+
 static struct imap_cmd *
 v_submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd,
                    const char *fmt, va_list ap )
@@ -503,11 +560,24 @@ v_submit_imap_cmd( imap_store_t *ctx, struct imap_cmd 
*cmd,
        const char *buffmt;
        char buf[1024];
 
-       while (ctx->literal_pending)
+       assert( ctx );
+       assert( cmd );
+       assert( cmd->param.done );
+
+       ctx->ref_count++;
+
+       while (ctx->literal_pending) {
                get_cmd_result( ctx, 0 );
+               if (ctx->store_canceled) {
+                       /* ctx (and thus the queue) is invalid by now. */
+                       cmd->param.done( 0, cmd, RESP_CANCEL );
+                       goto bail2;
+               }
+       }
+
+       if (ctx->buf.sock.fd < 0)
+               goto bail; /* We got disconnected and had no chance to report 
it yet. */
 
-       if (!cmd)
-               cmd = new_imap_cmd();
        cmd->tag = ++ctx->nexttag;
        if (fmt)
                nfvasprintf( &cmd->cmd, fmt, ap );
@@ -552,8 +622,11 @@ v_submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd,
        return cmd;
 
   bail:
+       cmd->param.done( ctx, cmd, RESP_BAD );
+  bail2:
        free( cmd->cmd );
        free( cmd );
+       deref_store( ctx );
        return NULL;
 }
 
@@ -569,54 +642,72 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd, 
const char *fmt, ... )
        return ret;
 }
 
-static int
-imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... )
+static void
+imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
+           void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response 
),
+           const char *fmt, ... )
 {
        va_list ap;
 
+       if (!cmdp)
+               cmdp = new_imap_cmd( sizeof(*cmdp) );
+       cmdp->param.done = done;
        va_start( ap, fmt );
        cmdp = v_submit_imap_cmd( ctx, cmdp, fmt, ap );
        va_end( ap );
        if (!cmdp)
-               return RESP_BAD;
+               return;
 
-       return get_cmd_result( ctx, cmdp );
+       get_cmd_result( ctx, cmdp );
 }
 
-static int
-imap_exec_b( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... )
+static void
+transform_box_response( int *response )
 {
-       va_list ap;
+       switch (*response) {
+       case RESP_CANCEL: *response = DRV_CANCELED; break;
+       case RESP_BAD: *response = DRV_STORE_BAD; break;
+       case RESP_NO: *response = DRV_BOX_BAD; break;
+       default: *response = DRV_OK; break;
+       }
+}
 
-       va_start( ap, fmt );
-       cmdp = v_submit_imap_cmd( ctx, cmdp, fmt, ap );
-       va_end( ap );
-       if (!cmdp)
-               return DRV_STORE_BAD;
+static void
+imap_done_simple_box( imap_store_t *ctx ATTR_UNUSED,
+                      struct imap_cmd *cmd, int response )
+{
+       struct imap_cmd_simple *cmdp = (struct imap_cmd_simple *)cmd;
 
-       switch (get_cmd_result( ctx, cmdp )) {
-       case RESP_BAD: return DRV_STORE_BAD;
-       case RESP_NO: return DRV_BOX_BAD;
-       default: return DRV_OK;
+       transform_box_response( &response );
+       cmdp->callback( response, cmdp->callback_aux );
+}
+
+static void
+transform_msg_response( int *response )
+{
+       switch (*response) {
+       case RESP_CANCEL: *response = DRV_CANCELED; break;
+       case RESP_BAD: *response = DRV_STORE_BAD; break;
+       case RESP_NO: *response = DRV_MSG_BAD; break;
+       default: *response = DRV_OK; break;
        }
 }
 
-static int
-imap_exec_m( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... )
+static void
+imap_done_simple_msg( imap_store_t *ctx ATTR_UNUSED,
+                      struct imap_cmd *cmd, int response )
 {
-       va_list ap;
+       struct imap_cmd_simple *cmdp = (struct imap_cmd_simple *)cmd;
 
-       va_start( ap, fmt );
-       cmdp = v_submit_imap_cmd( ctx, cmdp, fmt, ap );
-       va_end( ap );
-       if (!cmdp)
-               return DRV_STORE_BAD;
+       transform_msg_response( &response );
+       cmdp->callback( response, cmdp->callback_aux );
+}
 
-       switch (get_cmd_result( ctx, cmdp )) {
-       case RESP_BAD: return DRV_STORE_BAD;
-       case RESP_NO: return DRV_MSG_BAD;
-       default: return DRV_OK;
-       }
+static void
+imap_refcounted_done( struct imap_cmd_refcounted_state *sts )
+{
+       sts->callback( sts->ret_val, sts->callback_aux );
+       free( sts );
 }
 
 /*
@@ -628,12 +719,16 @@ drain_imap_replies( imap_store_t *ctx )
 }
 */
 
+/* call with a ref on ctx! */
 static void
 process_imap_replies( imap_store_t *ctx )
 {
        while (ctx->num_in_progress > max_in_progress ||
-              socket_pending( &ctx->buf.sock ))
+              socket_pending( &ctx->buf.sock )) {
                get_cmd_result( ctx, 0 );
+               if (ctx->store_canceled)
+                       break;
+       }
 }
 
 static int
@@ -861,7 +956,7 @@ parse_fetch( imap_store_t *ctx, char *cmd ) /* move this 
down */
                free_list( list );
                return -1;
          gotuid:
-               msgdata = (msg_data_t *)cmdp->param.aux;
+               msgdata = ((struct imap_cmd_fetch_msg *)cmdp)->msg_data;
                msgdata->data = body;
                msgdata->len = size;
                if (status & M_FLAGS)
@@ -929,10 +1024,11 @@ parse_response_code( imap_store_t *ctx, struct imap_cmd 
*cmd, char *s )
                 */
                for (; isspace( (unsigned char)*p ); p++);
                error( "*** IMAP ALERT *** %s\n", p );
-       } else if (cmd && cmd->param.aux && !strcmp( "APPENDUID", arg )) {
+       } else if (cmd && !strcmp( "APPENDUID", arg )) {
                if (!(arg = next_arg( &s )) ||
                    (ctx->gen.uidvalidity = strtoll( arg, &earg, 10 ), *earg) ||
-                   !(arg = next_arg( &s )) || !(*(int *)cmd->param.aux = atoi( 
arg )))
+                   !(arg = next_arg( &s )) ||
+                   !(((struct imap_cmd_out_uid *)cmd)->out_uid = atoi( arg )))
                {
                        error( "IMAP error: malformed APPENDUID status\n" );
                        return RESP_BAD;
@@ -964,7 +1060,7 @@ parse_search( imap_store_t *ctx, char *cmd )
         */
        for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next)
                if (cmdp->param.uid == -1) {
-                       *(int *)cmdp->param.aux = uid;
+                       ((struct imap_cmd_out_uid *)cmdp)->out_uid = uid;
                        return;
                }
        error( "IMAP error: unexpected SEARCH response (UID %u)\n", uid );
@@ -996,23 +1092,27 @@ parse_list_rsp( imap_store_t *ctx, char *cmd )
        add_string_list( &ctx->gen.boxes, arg );
 }
 
-static int
+struct imap_cmd_trycreate {
+       struct imap_cmd gen;
+       struct imap_cmd *orig_cmd;
+};
+
+static void get_cmd_result_p2( imap_store_t *, struct imap_cmd *, int );
+
+static void
 get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
 {
        struct imap_cmd *cmdp, **pcmdp;
        char *cmd, *arg, *arg1, *p;
        int n, resp, resp2, tag;
 
-       for (;;) {
-               if (buffer_gets( &ctx->buf, &cmd ))
-                       return RESP_BAD;
-
+       while (!buffer_gets( &ctx->buf, &cmd )) {
                arg = next_arg( &cmd );
                if (*arg == '*') {
                        arg = next_arg( &cmd );
                        if (!arg) {
                                error( "IMAP error: unable to parse untagged 
response\n" );
-                               return RESP_BAD;
+                               break;
                        }
 
                        if (!strcmp( "NAMESPACE", arg )) {
@@ -1035,15 +1135,15 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd 
*tcmd )
                                        ctx->gen.recent = atoi( arg );
                                else if(!strcmp ( "FETCH", arg1 )) {
                                        if (parse_fetch( ctx, cmd ))
-                                               return RESP_BAD;
+                                               break; /* stream is likely to 
be useless now */
                                }
                        } else {
-                               error( "IMAP error: unable to parse untagged 
response\n" );
-                               return RESP_BAD;
+                               error( "IMAP error: unrecognized untagged 
response '%s'\n", arg );
+                               break; /* this may mean anything, so prefer not 
to spam the log */
                        }
                } else if (!ctx->in_progress) {
                        error( "IMAP error: unexpected reply: %s %s\n", arg, 
cmd ? cmd : "" );
-                       return RESP_BAD;
+                       break; /* this may mean anything, so prefer not to spam 
the log */
                } else if (*arg == '+') {
                        /* This can happen only with the last command underway, 
as
                           it enforces a round-trip. */
@@ -1056,27 +1156,27 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd 
*tcmd )
                                free( cmdp->param.data );
                                cmdp->param.data = 0;
                                if (n != (int)cmdp->param.data_len)
-                                       return RESP_BAD;
+                                       break;
                        } else if (cmdp->param.cont) {
                                if (cmdp->param.cont( ctx, cmdp, cmd ))
-                                       return RESP_BAD;
+                                       break;
                        } else {
                                error( "IMAP error: unexpected command 
continuation request\n" );
-                               return RESP_BAD;
+                               break;
                        }
                        if (socket_write( &ctx->buf.sock, "\r\n", 2 ) != 2)
-                               return RESP_BAD;
+                               break;
                        if (!cmdp->param.cont)
                                ctx->literal_pending = 0;
                        if (!tcmd)
-                               return DRV_OK;
+                               return;
                } else {
                        tag = atoi( arg );
                        for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp 
= &cmdp->next)
                                if (cmdp->tag == tag)
                                        goto gottag;
                        error( "IMAP error: unexpected tag %s\n", arg );
-                       return RESP_BAD;
+                       break;
                  gottag:
                        if (!(*pcmdp = cmdp->next))
                                ctx->in_progress_append = pcmdp;
@@ -1087,24 +1187,25 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd 
*tcmd )
                        if (!strcmp( "OK", arg )) {
                                if (cmdp->param.to_trash)
                                        ctx->trashnc = 0; /* Can't get NO 
[TRYCREATE] any more. */
-                               resp = DRV_OK;
+                               resp = RESP_OK;
                        } else {
                                if (!strcmp( "NO", arg )) {
-                                       if (cmdp->param.create && cmd && 
(cmdp->param.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, 
APPEND or UID COPY */
+                                       if (cmdp->param.create &&
+                                           (cmdp->param.trycreate ||
+                                            (cmd && !memcmp( cmd, 
"[TRYCREATE]", 11 ))))
+                                       { /* SELECT, APPEND or UID COPY */
+                                               struct imap_cmd_trycreate *cmd2 
=
+                                                       (struct 
imap_cmd_trycreate *)new_imap_cmd( sizeof(*cmd2) );
+                                               cmd2->orig_cmd = cmdp;
+                                               cmd2->gen.param.done = 
get_cmd_result_p2;
+                                               ctx->ref_count++;
                                                p = strchr( cmdp->cmd, '"' );
-                                               if (!submit_imap_cmd( ctx, 0, 
"CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p )) {
-                                                       resp = RESP_BAD;
-                                                       goto normal;
-                                               }
-                                               /* not waiting here violates 
the spec, but a server that does not
-                                                  grok this nonetheless 
violates it too. */
-                                               cmdp->param.create = 0;
-                                               if (!submit_imap_cmd( ctx, 
cmdp, 0 )) {
-                                                       resp = RESP_BAD;
-                                                       goto normal;
-                                               }
+                                               submit_imap_cmd( ctx, 
&cmd2->gen, "CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p );
+                                               if (ctx->store_canceled)
+                                                       tcmd = 0;
+                                               deref_store( ctx );
                                                if (!tcmd)
-                                                       return 0;       /* 
ignored */
+                                                       return;
                                                continue;
                                        }
                                        resp = RESP_NO;
@@ -1116,17 +1217,52 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd 
*tcmd )
                        }
                        if ((resp2 = parse_response_code( ctx, cmdp, cmd )) > 
resp)
                                resp = resp2;
-                 normal:
-                       if (cmdp->param.done)
-                               cmdp->param.done( ctx, cmdp, resp );
+                       cmdp->param.done( ctx, cmdp, resp );
                        free( cmdp->param.data );
                        free( cmdp->cmd );
                        free( cmdp );
+                       if (ctx->store_canceled)
+                               tcmd = 0;
+                       deref_store( ctx );
                        if (!tcmd || tcmd == cmdp)
-                               return resp;
+                               return;
                }
        }
-       /* not reached */
+       /* Piggy-back the stream error on the next pending command. */
+       if ((cmdp = ctx->in_progress)) {
+               ctx->in_progress = cmdp->next; /* We're dead, so don't update 
in_progress_append */
+               ctx->num_in_progress--;
+               cmdp->param.done( ctx, cmdp, RESP_BAD );
+               free( cmdp->param.data );
+               free( cmdp->cmd );
+               free( cmdp );
+               deref_store( ctx );
+               return;
+       }
+       /* Made no callback, so let the next command submission fail. */
+       if (ctx->buf.sock.fd != -1) {
+               close( ctx->buf.sock.fd );
+               ctx->buf.sock.fd = -1;
+       }
+}
+
+static void
+get_cmd_result_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response )
+{
+       struct imap_cmd_trycreate *cmdp = (struct imap_cmd_trycreate *)cmd;
+       struct imap_cmd *ocmd = cmdp->orig_cmd;
+
+       if (response != RESP_OK) {
+               ocmd->param.done( ctx, ocmd, response );
+               free( ocmd->param.data );
+               free( ocmd->cmd );
+               free( ocmd );
+               deref_store( ctx );
+       } else {
+               ctx->uidnext = 0;
+               ocmd->param.create = 0;
+               submit_imap_cmd( ctx, ocmd, 0 );
+       }
 }
 
 static void
@@ -1151,7 +1287,8 @@ imap_cancel_store( store_t *gctx )
        free_list( ctx->ns_personal );
        free_list( ctx->ns_other );
        free_list( ctx->ns_shared );
-       free( ctx );
+       ctx->store_canceled = 1;
+       deref_store( ctx );
 }
 
 static store_t *unowned;
@@ -1178,6 +1315,8 @@ imap_own_store( store_conf_t *conf )
        return 0;
 }
 
+static void imap_cleanup_p2( imap_store_t *, struct imap_cmd *, int );
+
 static void
 imap_cleanup( void )
 {
@@ -1185,11 +1324,17 @@ imap_cleanup( void )
 
        for (ctx = unowned; ctx; ctx = nctx) {
                nctx = ctx->next;
-               imap_exec( (imap_store_t *)ctx, 0, "LOGOUT" );
-               imap_cancel_store( ctx );
+               imap_exec( (imap_store_t *)ctx, 0, imap_cleanup_p2, "LOGOUT" );
        }
 }
 
+static void
+imap_cleanup_p2( imap_store_t *ctx,
+                 struct imap_cmd *cmd ATTR_UNUSED, int response ATTR_UNUSED )
+{
+       imap_cancel_store( &ctx->gen );
+}
+
 #ifdef HAVE_LIBSSL
 static int
 start_tls( imap_store_t *ctx )
@@ -1298,6 +1443,19 @@ do_cram_auth( imap_store_t *ctx, struct imap_cmd *cmdp, 
const char *prompt )
 }
 #endif
 
+static void imap_open_store_p2( imap_store_t *, struct imap_cmd *, int );
+static void imap_open_store_authenticate( imap_store_t * );
+static void imap_open_store_authenticate_p2( imap_store_t *, struct imap_cmd 
*, int );
+static void imap_open_store_authenticate_p3( imap_store_t *, struct imap_cmd 
*, int );
+static void imap_open_store_authenticate2( imap_store_t * );
+static void imap_open_store_authenticate2_p2( imap_store_t *, struct imap_cmd 
*, int );
+static void imap_open_store_namespace( imap_store_t * );
+static void imap_open_store_namespace_p2( imap_store_t *, struct imap_cmd *, 
int );
+static void imap_open_store_namespace2( imap_store_t * );
+static void imap_open_store_finalize( imap_store_t * );
+static void imap_open_store_ssl_bail( imap_store_t * );
+static void imap_open_store_bail( imap_store_t * );
+
 static void
 imap_open_store( store_conf_t *conf,
                  void (*cb)( store_t *srv, void *aux ), void *aux )
@@ -1309,7 +1467,7 @@ imap_open_store( store_conf_t *conf,
        char *arg, *rsp;
        struct hostent *he;
        struct sockaddr_in addr;
-       int s, a[2], preauth;
+       int s, a[2];
 
        for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp = 
&ctx->gen.next)
                if (((imap_store_conf_t *)ctx->gen.conf)->server == srvc) {
@@ -1320,12 +1478,18 @@ imap_open_store( store_conf_t *conf,
                        ctx->gen.boxes = 0;
                        ctx->gen.listed = 0;
                        ctx->gen.conf = conf;
-                       goto final;
+                       ctx->callbacks.imap_open = cb;
+                       ctx->callback_aux = aux;
+                       imap_open_store_namespace( ctx );
+                       return;
                }
 
        ctx = nfcalloc( sizeof(*ctx) );
+       ctx->ref_count = 1;
        ctx->gen.conf = conf;
        ctx->buf.sock.fd = -1;
+       ctx->callbacks.imap_open = cb;
+       ctx->callback_aux = aux;
        ctx->in_progress_append = &ctx->in_progress;
 
        /* open connection to IMAP server */
@@ -1389,8 +1553,10 @@ imap_open_store( store_conf_t *conf,
 
 #if HAVE_LIBSSL
        if (srvc->use_imaps) {
-               if (start_tls( ctx ))
-                       goto ssl_bail;
+               if (start_tls( ctx )) {
+                       imap_open_store_ssl_bail( ctx );
+                       return;
+               }
        }
 #endif
 
@@ -1402,121 +1568,217 @@ imap_open_store( store_conf_t *conf,
                error( "IMAP error: invalid greeting response\n" );
                goto bail;
        }
-       preauth = 0;
        if (!strcmp( "PREAUTH", arg ))
-               preauth = 1;
+               ctx->preauth = 1;
        else if (strcmp( "OK", arg ) != 0) {
                error( "IMAP error: unknown greeting response\n" );
                goto bail;
        }
        parse_response_code( ctx, 0, rsp );
-       if (!ctx->caps && imap_exec( ctx, 0, "CAPABILITY" ) != RESP_OK)
-               goto bail;
+       if (!ctx->caps)
+               imap_exec( ctx, 0, imap_open_store_p2, "CAPABILITY" );
+       else
+               imap_open_store_authenticate( ctx );
+       return;
+
+  bail:
+       imap_open_store_bail( ctx );
+}
 
-       if (!preauth) {
+static void
+imap_open_store_p2( imap_store_t *ctx, struct imap_cmd *cmd ATTR_UNUSED, int 
response )
+{
+       if (response != RESP_OK)
+               imap_open_store_bail( ctx );
+       else
+               imap_open_store_authenticate( ctx );
+}
+
+static void
+imap_open_store_authenticate( imap_store_t *ctx )
+{
+       if (!ctx->preauth) {
 #if HAVE_LIBSSL
+               imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
+               imap_server_conf_t *srvc = cfg->server;
+
                if (!srvc->use_imaps && (srvc->use_sslv2 || srvc->use_sslv3 || 
srvc->use_tlsv1)) {
                        /* always try to select SSL support if available */
                        if (CAP(STARTTLS)) {
-                               if (imap_exec( ctx, 0, "STARTTLS" ) != RESP_OK)
-                                       goto bail;
-                               if (start_tls( ctx ))
-                                       goto ssl_bail;
-
-                               if (imap_exec( ctx, 0, "CAPABILITY" ) != 
RESP_OK)
-                                       goto bail;
+                               imap_exec( ctx, 0, 
imap_open_store_authenticate_p2, "STARTTLS" );
+                               return;
                        } else {
                                if (srvc->require_ssl) {
                                        error( "IMAP error: SSL support not 
available\n" );
-                                       goto bail;
-                               } else
+                                       imap_open_store_bail( ctx );
+                                       return;
+                               } else {
                                        warn( "IMAP warning: SSL support not 
available\n" );
+                               }
                        }
                }
 #endif
+               imap_open_store_authenticate2( ctx );
+       } else {
+               imap_open_store_namespace( ctx );
+       }
+}
 
-               info ("Logging in...\n");
-               if (!srvc->user) {
-                       error( "Skipping account %s, no user\n", srvc->name );
-                       goto bail;
+#if HAVE_LIBSSL
+static void
+imap_open_store_authenticate_p2( imap_store_t *ctx, struct imap_cmd *cmd 
ATTR_UNUSED, int response )
+{
+       if (response != RESP_OK)
+               imap_open_store_bail( ctx );
+       else if (start_tls( ctx ))
+               imap_open_store_ssl_bail( ctx );
+       else
+               imap_exec( ctx, 0, imap_open_store_authenticate_p3, 
"CAPABILITY" );
+}
+
+static void
+imap_open_store_authenticate_p3( imap_store_t *ctx, struct imap_cmd *cmd 
ATTR_UNUSED, int response )
+{
+       if (response != RESP_OK)
+               imap_open_store_bail( ctx );
+       else
+               imap_open_store_authenticate2( ctx );
+}
+#endif
+
+static void
+imap_open_store_authenticate2( imap_store_t *ctx )
+{
+       imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
+       imap_server_conf_t *srvc = cfg->server;
+       char *arg;
+
+       info ("Logging in...\n");
+       if (!srvc->user) {
+               error( "Skipping account %s, no user\n", srvc->name );
+               goto bail;
+       }
+       if (!srvc->pass) {
+               char prompt[80];
+               sprintf( prompt, "Password (%s): ", srvc->name );
+               arg = getpass( prompt );
+               if (!arg) {
+                       perror( "getpass" );
+                       exit( 1 );
                }
-               if (!srvc->pass) {
-                       char prompt[80];
-                       sprintf( prompt, "Password (%s): ", srvc->name );
-                       arg = getpass( prompt );
-                       if (!arg) {
-                               perror( "getpass" );
-                               exit( 1 );
-                       }
-                       if (!*arg) {
-                               error( "Skipping account %s, no password\n", 
srvc->name );
-                               goto bail;
-                       }
-                       /*
-                        * getpass() returns a pointer to a static buffer.  
make a copy
-                        * for long term storage.
-                        */
-                       srvc->pass = nfstrdup( arg );
+               if (!*arg) {
+                       error( "Skipping account %s, no password\n", srvc->name 
);
+                       goto bail;
                }
+               /*
+                * getpass() returns a pointer to a static buffer.  make a copy
+                * for long term storage.
+                */
+               srvc->pass = nfstrdup( arg );
+       }
 #if HAVE_LIBSSL
-               if (CAP(CRAM)) {
-                       struct imap_cmd *cmd = new_imap_cmd();
+       if (CAP(CRAM)) {
+               struct imap_cmd *cmd = new_imap_cmd( sizeof(*cmd) );
 
-                       info( "Authenticating with CRAM-MD5\n" );
-                       cmd->param.cont = do_cram_auth;
-                       if (imap_exec( ctx, cmd, "AUTHENTICATE CRAM-MD5" ) != 
RESP_OK)
-                               goto bail;
-               } else if (srvc->require_cram) {
-                       error( "IMAP error: CRAM-MD5 authentication is not 
supported by server\n" );
-                       goto bail;
-               } else
+               info( "Authenticating with CRAM-MD5\n" );
+               cmd->param.cont = do_cram_auth;
+               imap_exec( ctx, cmd, imap_open_store_authenticate2_p2, 
"AUTHENTICATE CRAM-MD5" );
+               return;
+       }
+       if (srvc->require_cram) {
+               error( "IMAP error: CRAM-MD5 authentication is not supported by 
server\n" );
+               goto bail;
+       }
 #endif
-               {
-                       if (CAP(NOLOGIN)) {
-                               error( "Skipping account %s, server forbids 
LOGIN\n", srvc->name );
-                               goto bail;
-                       }
+       if (CAP(NOLOGIN)) {
+               error( "Skipping account %s, server forbids LOGIN\n", 
srvc->name );
+               goto bail;
+       }
 #if HAVE_LIBSSL
-                       if (!ctx->buf.sock.use_ssl)
+       if (!ctx->buf.sock.use_ssl)
 #endif
-                               warn( "*** IMAP Warning *** Password is being 
sent in the clear\n" );
-                       if (imap_exec( ctx, 0, "LOGIN \"%s\" \"%s\"", 
srvc->user, srvc->pass ) != RESP_OK) {
-                               error( "IMAP error: LOGIN failed\n" );
-                               goto bail;
-                       }
-               }
-       } /* !preauth */
+               warn( "*** IMAP Warning *** Password is being sent in the 
clear\n" );
+       imap_exec( ctx, 0, imap_open_store_authenticate2_p2,
+                  "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass );
+       return;
+
+  bail:
+       imap_open_store_bail( ctx );
+}
+
+static void
+imap_open_store_authenticate2_p2( imap_store_t *ctx, struct imap_cmd *cmd 
ATTR_UNUSED, int response )
+{
+       if (response != RESP_OK)
+               imap_open_store_bail( ctx );
+       else
+               imap_open_store_namespace( ctx );
+}
+
+static void
+imap_open_store_namespace( imap_store_t *ctx )
+{
+       imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
 
-  final:
        ctx->prefix = "";
-       if (*conf->path)
-               ctx->prefix = conf->path;
+       if (*cfg->gen.path)
+               ctx->prefix = cfg->gen.path;
        else if (cfg->use_namespace && CAP(NAMESPACE)) {
                /* get NAMESPACE info */
-               if (!ctx->got_namespace) {
-                       if (imap_exec( ctx, 0, "NAMESPACE" ) != RESP_OK) {
-                               cb( 0, aux );
-                               return;
-                       }
-                       ctx->got_namespace = 1;
-               }
-               /* XXX for now assume personal namespace */
-               if (is_list( ctx->ns_personal ) &&
-                   is_list( ctx->ns_personal->child ) &&
-                   is_atom( ctx->ns_personal->child->child ))
-                       ctx->prefix = ctx->ns_personal->child->child->val;
+               if (!ctx->got_namespace)
+                       imap_exec( ctx, 0, imap_open_store_namespace_p2, 
"NAMESPACE" );
+               else
+                       imap_open_store_namespace2( ctx );
+               return;
        }
+       imap_open_store_finalize( ctx );
+}
+
+static void
+imap_open_store_namespace_p2( imap_store_t *ctx, struct imap_cmd *cmd 
ATTR_UNUSED, int response )
+{
+       if (response != RESP_OK) {
+               imap_open_store_bail( ctx );
+       } else {
+               ctx->got_namespace = 1;
+               imap_open_store_namespace2( ctx );
+       }
+}
+
+static void
+imap_open_store_namespace2( imap_store_t *ctx )
+{
+       /* XXX for now assume personal namespace */
+       if (is_list( ctx->ns_personal ) &&
+           is_list( ctx->ns_personal->child ) &&
+           is_atom( ctx->ns_personal->child->child ))
+               ctx->prefix = ctx->ns_personal->child->child->val;
+       imap_open_store_finalize( ctx );
+}
+
+static void
+imap_open_store_finalize( imap_store_t *ctx )
+{
        ctx->trashnc = 1;
-       cb( &ctx->gen, aux );
-       return;
+       ctx->callbacks.imap_open( &ctx->gen, ctx->callback_aux );
+}
 
 #if HAVE_LIBSSL
-  ssl_bail:
+static void
+imap_open_store_ssl_bail( imap_store_t *ctx )
+{
        /* This avoids that we try to send LOGOUT to an unusable socket. */
        close( ctx->buf.sock.fd );
        ctx->buf.sock.fd = -1;
+       imap_open_store_bail( ctx );
+}
 #endif
-  bail:
+
+static void
+imap_open_store_bail( imap_store_t *ctx )
+{
+       void (*cb)( store_t *srv, void *aux ) = ctx->callbacks.imap_open;
+       void *aux = ctx->callback_aux;
        imap_cancel_store( &ctx->gen );
        cb( 0, aux );
 }
@@ -1534,16 +1796,23 @@ imap_prepare_opts( store_t *gctx, int opts )
        gctx->opts = opts;
 }
 
+struct imap_cmd_select {
+       struct imap_cmd_simple gen;
+       int minuid, maxuid, *excs, nexcs;
+};
+
+static void imap_select_p2( imap_store_t *, struct imap_cmd *, int );
+static void imap_submit_select2( imap_store_t *, const char *, struct 
imap_cmd_refcounted_state *,
+                                 struct imap_cmd_refcounted ** );
+static void imap_select2_p2( imap_store_t *, struct imap_cmd *, int );
+
 static void
 imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs,
              void (*cb)( int sts, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       struct imap_cmd *cmd = new_imap_cmd();
+       struct imap_cmd_select *cmd;
        const char *prefix;
-       int ret, i, j, bl;
-       char buf[1000];
-
 
        if (!strcmp( gctx->name, "INBOX" )) {
                prefix = "";
@@ -1553,56 +1822,130 @@ imap_select( store_t *gctx, int minuid, int maxuid, 
int *excs, int nexcs,
 
        ctx->uidnext = -1;
 
-       cmd->param.create = (gctx->opts & OPEN_CREATE) != 0;
-       cmd->param.trycreate = 1;
-       if ((ret = imap_exec_b( ctx, cmd, "SELECT \"%s%s\"", prefix, gctx->name 
)) != DRV_OK)
-               goto bail;
+       INIT_IMAP_CMD_X(imap_cmd_select, cmd, cb, aux)
+       cmd->gen.gen.param.create = (gctx->opts & OPEN_CREATE) != 0;
+       cmd->gen.gen.param.trycreate = 1;
+       cmd->minuid = minuid;
+       cmd->maxuid = maxuid;
+       cmd->excs = excs;
+       cmd->nexcs = nexcs;
+       imap_exec( ctx, &cmd->gen.gen, imap_select_p2,
+                  "SELECT \"%s%s\"", prefix, gctx->name );
+}
 
-       if (gctx->count) {
-               ctx->msgapp = &gctx->msgs;
-               sort_ints( excs, nexcs );
-               for (i = 0; i < nexcs; ) {
-                       for (bl = 0; i < nexcs && bl < 960; i++) {
+static void
+imap_select_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response )
+{
+       struct imap_cmd_select *cmdp = (struct imap_cmd_select *)cmd;
+       int i, j, bl;
+       char buf[1000];
+
+       transform_box_response( &response );
+       if (response != DRV_OK || !ctx->gen.count) {
+               free( cmdp->excs );
+               cmdp->gen.callback( response, cmdp->gen.callback_aux );
+       } else {
+               struct imap_cmd_refcounted *cmd2 = 0;
+               struct imap_cmd_refcounted_state *sts = nfmalloc( sizeof(*sts) 
);
+               sts->callback = cmdp->gen.callback;
+               sts->callback_aux = cmdp->gen.callback_aux;
+               sts->ref_count = 1; /* so forced sync does not cause an early 
exit */
+               sts->ret_val = DRV_OK;
+               ctx->ref_count++;
+
+               ctx->msgapp = &ctx->gen.msgs;
+               sort_ints( cmdp->excs, cmdp->nexcs );
+               for (i = 0; i < cmdp->nexcs; ) {
+                       for (bl = 0; i < cmdp->nexcs && bl < 960; i++) {
                                if (bl)
                                        buf[bl++] = ',';
-                               bl += sprintf( buf + bl, "%d", excs[i] );
+                               bl += sprintf( buf + bl, "%d", cmdp->excs[i] );
                                j = i;
-                               for (; i + 1 < nexcs && excs[i + 1] == excs[i] 
+ 1; i++);
+                               for (; i + 1 < cmdp->nexcs && cmdp->excs[i + 1] 
== cmdp->excs[i] + 1; i++);
                                if (i != j)
-                                       bl += sprintf( buf + bl, ":%d", excs[i] 
);
+                                       bl += sprintf( buf + bl, ":%d", 
cmdp->excs[i] );
+                       }
+                       imap_submit_select2( ctx, buf, sts, &cmd2 );
+                       if (ctx->store_canceled) {
+                               deref_store( ctx );
+                               free( cmdp->excs );
+                               return;
                        }
-                       if ((ret = imap_exec_b( ctx, 0, "UID FETCH %s 
(UID%s%s)", buf,
-                                               (gctx->opts & OPEN_FLAGS) ? " 
FLAGS" : "",
-                                               (gctx->opts & OPEN_SIZE) ? " 
RFC822.SIZE" : "" )) != DRV_OK)
-                               goto bail;
                }
-               if (maxuid == INT_MAX)
-                       maxuid = ctx->uidnext >= 0 ? ctx->uidnext - 1 : 
1000000000;
-               if (maxuid >= minuid &&
-                   (ret = imap_exec_b( ctx, 0, "UID FETCH %d:%d (UID%s%s)", 
minuid, maxuid,
-                                       (gctx->opts & OPEN_FLAGS) ? " FLAGS" : 
"",
-                                       (gctx->opts & OPEN_SIZE) ? " 
RFC822.SIZE" : "" )) != DRV_OK)
-                       goto bail;
+               if (cmdp->maxuid == INT_MAX)
+                       cmdp->maxuid = ctx->uidnext >= 0 ? ctx->uidnext - 1 : 
1000000000;
+               if (cmdp->maxuid >= cmdp->minuid) {
+                       sprintf( buf, "%d:%d", cmdp->minuid, cmdp->maxuid );
+                       imap_submit_select2( ctx, buf, sts, &cmd2 );
+                       if (ctx->store_canceled) {
+                               deref_store( ctx );
+                               free( cmdp->excs );
+                               return;
+                       }
+               }
+               deref_store( ctx );
+               free( cmdp->excs );
+               if (!--sts->ref_count)
+                       imap_refcounted_done( sts );
+               else
+                       get_cmd_result( ctx, &cmd2->gen );
        }
+}
+
+static void
+imap_submit_select2( imap_store_t *ctx, const char *buf, struct 
imap_cmd_refcounted_state *sts,
+                     struct imap_cmd_refcounted **cmdp )
+{
+       struct imap_cmd_refcounted *cmd = (struct imap_cmd_refcounted 
*)new_imap_cmd( sizeof(*cmd) );
+       cmd->gen.param.done = imap_select2_p2;
+       cmd->state = sts;
+       sts->ref_count++;
+       *cmdp = cmd;
+       submit_imap_cmd( ctx, &cmd->gen,
+                        "UID FETCH %s (UID%s%s)", buf,
+                        (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "",
+                        (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "" );
+}
 
-       ret = DRV_OK;
+static void
+imap_select2_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int 
response )
+{
+       struct imap_cmd_refcounted_state *sts = ((struct imap_cmd_refcounted 
*)cmd)->state;
 
-  bail:
-       free( excs );
-       cb( ret, aux );
+       switch (response) {
+       case RESP_CANCEL:
+               /* sts was already free()d by the BAD callback. We can do that, 
because we know
+                * that our fetches are the only commands in flight, so nothing 
else can cause a
+                * cancelation. */
+               return;
+       case RESP_BAD:
+               sts->ret_val = DRV_STORE_BAD;
+               imap_refcounted_done( sts );
+               return;
+       case RESP_NO:
+               sts->ret_val = DRV_BOX_BAD;
+               break;
+       }
+       if (!--sts->ref_count)
+               imap_refcounted_done( sts );
 }
 
 static void
 imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data,
                 void (*cb)( int sts, void *aux ), void *aux )
 {
-       struct imap_cmd *cmd = new_imap_cmd();
-       cmd->param.uid = msg->uid;
-       cmd->param.aux = data;
-       cb( imap_exec_m( (imap_store_t *)ctx, cmd, "UID FETCH %d 
(%sBODY.PEEK[])",
-                        msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " ), 
aux );
+       struct imap_cmd_fetch_msg *cmd;
+
+       INIT_IMAP_CMD_X(imap_cmd_fetch_msg, cmd, cb, aux)
+       cmd->gen.gen.param.uid = msg->uid;
+       cmd->msg_data = data;
+       imap_exec( (imap_store_t *)ctx, &cmd->gen.gen, imap_done_simple_msg,
+                  "UID FETCH %d (%sBODY.PEEK[])",
+                  msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " );
 }
 
+static void imap_set_flags_p2( imap_store_t *, struct imap_cmd *, int );
+
 static int
 imap_make_flags( int flags, char *buf )
 {
@@ -1622,15 +1965,18 @@ imap_make_flags( int flags, char *buf )
 }
 
 static int
-imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags)
+imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags,
+                   struct imap_cmd_refcounted_state *sts )
 {
        char buf[256];
 
+       struct imap_cmd_refcounted *cmd = (struct imap_cmd_refcounted 
*)new_imap_cmd( sizeof(*cmd) );
+       cmd->gen.param.done = imap_set_flags_p2;
+       cmd->state = sts;
+       sts->ref_count++;
        buf[imap_make_flags( flags, buf )] = 0;
-       if (!submit_imap_cmd( ctx, 0, "UID STORE %d %cFLAGS.SILENT %s", uid, 
what, buf ))
-               return DRV_STORE_BAD;
-       process_imap_replies( ctx );
-       return DRV_OK;
+       submit_imap_cmd( ctx, &cmd->gen, "UID STORE %d %cFLAGS.SILENT %s", uid, 
what, buf );
+       return ctx->store_canceled;
 }
 
 static void
@@ -1638,7 +1984,6 @@ imap_set_flags( store_t *gctx, message_t *msg, int uid, 
int add, int del,
                 void (*cb)( int sts, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       int ret;
 
        if (msg) {
                uid = msg->uid;
@@ -1647,17 +1992,66 @@ imap_set_flags( store_t *gctx, message_t *msg, int uid, 
int add, int del,
                msg->flags |= add;
                msg->flags &= ~del;
        }
-       if ((!add || (ret = imap_flags_helper( ctx, uid, '+', add )) == DRV_OK) 
&&
-           (!del || (ret = imap_flags_helper( ctx, uid, '-', del )) == DRV_OK))
-               ret = DRV_OK;
-       cb( ret, aux );
+
+       if (add || del) {
+               struct imap_cmd_refcounted_state *sts = nfmalloc( sizeof(*sts) 
);
+               sts->callback = cb;
+               sts->callback_aux = aux;
+               sts->ref_count = 1; /* so forced sync does not cause an early 
exit */
+               sts->ret_val = DRV_OK;
+               ctx->ref_count++;
+               if ((add && imap_flags_helper( ctx, uid, '+', add, sts )) ||
+                   (del && imap_flags_helper( ctx, uid, '-', del, sts )))
+               {
+                       deref_store( ctx );
+                       if (!--sts->ref_count)
+                               free( sts );
+                       return;
+               }
+               if (!--sts->ref_count)
+                       imap_refcounted_done( sts );
+               else
+                       process_imap_replies( ctx );
+               deref_store( ctx );
+       } else {
+               cb( DRV_OK, aux );
+       }
+}
+
+static void
+imap_set_flags_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int 
response )
+{
+       struct imap_cmd_refcounted_state *sts = ((struct imap_cmd_refcounted 
*)cmd)->state;
+       switch (response) {
+       case RESP_CANCEL:
+               if (sts->ret_val == DRV_STORE_BAD)
+                       goto badout; /* Our own command's bad-store callback 
caused cancelation. */
+               sts->ret_val = DRV_CANCELED; /* Unrelated command caused 
cancelation. */
+               break;
+       case RESP_BAD:
+               sts->ret_val = DRV_STORE_BAD;
+               sts->callback( sts->ret_val, sts->callback_aux );
+         badout:
+               if (!--sts->ref_count)
+                       free( sts );
+               return;
+       case RESP_NO:
+               if (sts->ret_val == DRV_OK) /* Don't override cancelation. */
+                       sts->ret_val = DRV_MSG_BAD;
+               break;
+       }
+       if (!--sts->ref_count)
+               imap_refcounted_done( sts );
 }
 
 static void
 imap_close( store_t *ctx,
             void (*cb)( int sts, void *aux ), void *aux )
 {
-       cb( imap_exec_b( (imap_store_t *)ctx, 0, "CLOSE" ), aux );
+       struct imap_cmd_simple *cmd;
+
+       INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
+       imap_exec( (imap_store_t *)ctx, &cmd->gen, imap_done_simple_box, 
"CLOSE" );
 }
 
 static void
@@ -1665,21 +2059,26 @@ imap_trash_msg( store_t *gctx, message_t *msg,
                 void (*cb)( int sts, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       struct imap_cmd *cmd = new_imap_cmd();
-       cmd->param.create = 1;
-       cmd->param.to_trash = 1;
-       cb( imap_exec_m( ctx, cmd, "UID COPY %d \"%s%s\"",
-                        msg->uid, ctx->prefix, gctx->conf->trash ), aux );
+       struct imap_cmd_simple *cmd;
+
+       INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
+       cmd->gen.param.create = 1;
+       cmd->gen.param.to_trash = 1;
+       imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
+                  "UID COPY %d \"%s%s\"",
+                  msg->uid, ctx->prefix, gctx->conf->trash );
 }
 
+static void imap_store_msg_p2( imap_store_t *, struct imap_cmd *, int );
+
 static void
 imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
                 void (*cb)( int sts, int uid, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       struct imap_cmd *cmd = new_imap_cmd();
+       struct imap_cmd_out_uid *cmd;
        const char *prefix, *box;
-       int ret, d, uid;
+       int d;
        char flagstr[128];
 
        d = 0;
@@ -1689,53 +2088,91 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int 
to_trash,
        }
        flagstr[d] = 0;
 
-       cmd->param.data_len = data->len;
-       cmd->param.data = data->data;
-       cmd->param.aux = &uid;
-       uid = -2;
+       INIT_IMAP_CMD(imap_cmd_out_uid, cmd, cb, aux)
+       cmd->gen.param.data_len = data->len;
+       cmd->gen.param.data = data->data;
+       cmd->out_uid = -1;
 
        if (to_trash) {
                box = gctx->conf->trash;
                prefix = ctx->prefix;
-               cmd->param.create = 1;
-               cmd->param.to_trash = 1;
+               cmd->gen.param.create = 1;
+               cmd->gen.param.to_trash = 1;
        } else {
                box = gctx->name;
                prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
        }
-       ret = imap_exec_m( ctx, cmd, "APPEND \"%s%s\" %s", prefix, box, flagstr 
);
-       if (ret != DRV_OK)
-               uid = -1;
-       cb( ret, uid, aux );
+       imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
+                  "APPEND \"%s%s\" %s", prefix, box, flagstr );
+}
+
+static void
+imap_store_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int 
response )
+{
+       struct imap_cmd_out_uid *cmdp = (struct imap_cmd_out_uid *)cmd;
+
+       transform_msg_response( &response );
+       cmdp->callback( response, cmdp->out_uid, cmdp->callback_aux );
 }
 
+static void imap_find_msg_p2( imap_store_t *, struct imap_cmd *, int );
+
 static void
 imap_find_msg( store_t *gctx, const char *tuid,
                void (*cb)( int sts, int uid, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       struct imap_cmd *cmd = new_imap_cmd();
-       int ret, uid;
-
-       cmd->param.uid = -1; /* we're looking for a UID */
-       cmd->param.aux = &uid;
-       uid = -1; /* in case we get no SEARCH response at all */
-       if ((ret = imap_exec_m( ctx, cmd, "UID SEARCH HEADER X-TUID %." 
stringify(TUIDL) "s", tuid )) != DRV_OK)
-               cb( ret, -1, aux );
+       struct imap_cmd_out_uid *cmd;
+
+       INIT_IMAP_CMD(imap_cmd_out_uid, cmd, cb, aux)
+       cmd->gen.param.uid = -1; /* we're looking for a UID */
+       cmd->out_uid = -1; /* in case we get no SEARCH response at all */
+       imap_exec( ctx, &cmd->gen, imap_find_msg_p2,
+                  "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid );
+}
+
+static void
+imap_find_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int 
response )
+{
+       struct imap_cmd_out_uid *cmdp = (struct imap_cmd_out_uid *)cmd;
+
+       transform_msg_response( &response );
+       if (response != DRV_OK)
+               cmdp->callback( response, -1, cmdp->callback_aux );
        else
-               cb( uid <= 0 ? DRV_MSG_BAD : DRV_OK, uid, aux );
+               cmdp->callback( cmdp->out_uid <= 0 ? DRV_MSG_BAD : DRV_OK,
+                               cmdp->out_uid, cmdp->callback_aux );
 }
 
+struct imap_cmd_list {
+       struct imap_cmd gen;
+       void (*callback)( int sts, void *aux );
+       void *callback_aux;
+};
+
+static void imap_list_p2( imap_store_t *, struct imap_cmd *, int );
+
 static void
 imap_list( store_t *gctx,
            void (*cb)( int sts, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       int ret;
+       struct imap_cmd_list *cmd;
+
+       INIT_IMAP_CMD(imap_cmd_list, cmd, cb, aux)
+       imap_exec( ctx, &cmd->gen, imap_list_p2,
+                  "LIST \"\" \"%s%%\"", ctx->prefix );
+}
+
+static void
+imap_list_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response )
+{
+       struct imap_cmd_list *cmdp = (struct imap_cmd_list *)cmd;
 
-       if ((ret = imap_exec_b( ctx, 0, "LIST \"\" \"%s%%\"", ctx->prefix )) == 
DRV_OK)
-               gctx->listed = 1;
-       cb( ret, aux );
+       transform_box_response( &response );
+       if (response == DRV_OK)
+               ctx->gen.listed = 1;
+       cmdp->callback( response, cmdp->callback_aux );
 }
 
 static void

------------------------------------------------------------------------------
Colocation vs. Managed Hosting
A question and answer guide to determining the best fit
for your organization - today and in the future.
http://p.sf.net/sfu/internap-sfd2d
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to