commit 4cf90cedca8823aa7c2a49431c7681518e7dc7ba Author: Oswald Buddenhagen <o...@kde.org> Date: Sat Feb 6 10:25:49 2010 +0100
fully aync operation, take 2 *** WIP *** second attempt, starting "from the other end". this branch will be subject to forced pushes. TODO | 11 + src/drv_imap.c | 667 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 501 insertions(+), 177 deletions(-) diff --git a/TODO b/TODO index dd2aafc..b81f0ba 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,12 @@ +imap: +- kill the rcaps hack. make to_trash a generic option. make submit_imap_cmd + act upon it and the resp. *nc option. reset resp. *nc when the data is sent + (as at that point the command will not fail with TRYCREATE any more). +- rename cont to data_callback +- store_msg (to trash) and trash_msg commands must be queued until trashnc is 0, + otherwise they could be executed before the create and run into the same NO +- do something with the int returned by the callbacks + make SSL certificate validation more automatic. add deamon mode. primary goal: keep imap password in memory. @@ -18,6 +27,8 @@ flagging the dummy would fetch the real message. possibly remove --renew. don't SELECT boxes unless really needed; in particular not for appending, and in write-only mode not before changes are made. +problem: UIDVALIDITY change detection is delayed, significantly complicating +matters. possibly request message attributes on a per-message basis from the drivers. considerations: diff --git a/src/drv_imap.c b/src/drv_imap.c index dc90678..cb245c2 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -111,12 +111,12 @@ struct imap_cmd; typedef struct imap_store { store_t gen; const char *prefix; - unsigned /*currentnc:1,*/ trashnc:1; - int uidnext; /* from SELECT responses */ + unsigned /*currentnc:1,*/ trashnc:1; /* suppress LITERAL+ use on first STORE */ unsigned got_namespace:1; + int uidnext; /* from SELECT responses */ list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */ message_t **msgapp; /* FETCH results */ - unsigned caps, rcaps; /* CAPABILITY results */ + unsigned caps; /* CAPABILITY results */ /* command queue */ int nexttag, num_in_progress, literal_pending; struct imap_cmd *in_progress, **in_progress_append; @@ -134,16 +134,42 @@ 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 */ + int uid; /* to identify fetch & search responses */ unsigned + to_trash:1, /* we are storing to trash, not current. */ create:1, /* create the mailbox if we get an error ... */ trycreate:1; /* ... but only if this is true or the server says so. */ } param; }; +struct imap_cmd_simple { + struct imap_cmd gen; + int (*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_store_msg { + struct imap_cmd gen; + int (*callback)( int sts, int uid, void *aux ); + void *callback_aux; + int out_uid; +}; + +// XXX merge with above: imap_cmd_out_uid +struct imap_cmd_find_msg { + struct imap_cmd gen; + int (*callback)( int sts, int uid, void *aux ); + void *callback_aux; + int out_uid; +}; + #define CAP(cap) (ctx->caps & (1 << (cap))) enum CAPABILITY { @@ -168,9 +194,10 @@ static const char *cap_list[] = { #endif }; -#define RESP_OK 0 -#define RESP_NO 1 -#define RESP_BAD 2 +#define RESP_OK 0 +#define RESP_TRYCREATE 1 +#define RESP_NO 2 +#define RESP_BAD 3 static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ); @@ -476,29 +503,49 @@ buffer_gets( buffer_t * b, char **s ) } 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 ) { - int n, bufl; + int n, bufl, litplus; + const char *buffmt; char buf[1024]; while (ctx->literal_pending) get_cmd_result( ctx, 0 ); if (!cmd) - cmd = new_imap_cmd(); + cmd = new_imap_cmd( sizeof(*cmd) ); cmd->tag = ++ctx->nexttag; nfvasprintf( &cmd->cmd, fmt, ap ); - bufl = nfsnprintf( buf, sizeof(buf), cmd->param.data ? CAP(LITERALPLUS) ? - "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n", + if (!cmd->param.data) { + buffmt = "%d %s\r\n"; + litplus = 0; + } else if ((cmd->param.to_trash && ctx->trashnc) || !CAP(LITERALPLUS)) { + buffmt = "%d %s{%d}\r\n"; + litplus = 0; + } else { + buffmt = "%d %s{%d+}\r\n"; + litplus = 1; + } + bufl = nfsnprintf( buf, sizeof(buf), buffmt, cmd->tag, cmd->cmd, cmd->param.data_len ); if (DFlags & VERBOSE) { if (ctx->num_in_progress) @@ -511,31 +558,30 @@ v_submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd, if (socket_write( &ctx->buf.sock, buf, bufl ) != bufl) { if (cmd->param.data) free( cmd->param.data ); - free( cmd->cmd ); - free( cmd ); - return NULL; + goto bail; } - if (cmd->param.data) { - if (CAP(LITERALPLUS)) { - n = socket_write( &ctx->buf.sock, cmd->param.data, cmd->param.data_len ); - free( cmd->param.data ); - if (n != cmd->param.data_len || - (n = socket_write( &ctx->buf.sock, "\r\n", 2 )) != 2) - { - free( cmd->cmd ); - free( cmd ); - return NULL; - } - cmd->param.data = 0; - } else - ctx->literal_pending = 1; - } else if (cmd->param.cont) + if (litplus) { + n = socket_write( &ctx->buf.sock, cmd->param.data, cmd->param.data_len ); + free( cmd->param.data ); + if (n != cmd->param.data_len || + (n = socket_write( &ctx->buf.sock, "\r\n", 2 )) != 2) + goto bail; + cmd->param.data = 0; + } else if (cmd->param.cont || cmd->param.data) { ctx->literal_pending = 1; + } cmd->next = 0; *ctx->in_progress_append = cmd; ctx->in_progress_append = &cmd->next; ctx->num_in_progress++; return cmd; + + bail: + if (cmd->param.done) + cmd->param.done( ctx, cmd, RESP_BAD ); + free( cmd->cmd ); + free( cmd ); + return NULL; } static struct imap_cmd * @@ -550,22 +596,23 @@ 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_run( 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; + 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 get_cmd_result( ctx, cmdp ); + if (cmdp) + get_cmd_result( ctx, cmdp ); } static int -imap_exec_b( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... ) +imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... ) { va_list ap; @@ -573,33 +620,51 @@ imap_exec_b( imap_store_t *ctx, struct imap_cmd *cmdp, const char *fmt, ... ) cmdp = v_submit_imap_cmd( ctx, cmdp, fmt, ap ); va_end( ap ); if (!cmdp) - return DRV_STORE_BAD; + return RESP_BAD; - switch (get_cmd_result( ctx, cmdp )) { - case RESP_BAD: return DRV_STORE_BAD; - case RESP_NO: return DRV_BOX_BAD; - default: return DRV_OK; + return get_cmd_result( ctx, cmdp ); +} + +static void +transform_box_response( int *response ) +{ + switch (*response) { + case RESP_BAD: *response = DRV_STORE_BAD; break; + case RESP_NO: *response = DRV_BOX_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_box( 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_box_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 +transform_msg_response( int *response ) +{ + switch (*response) { + case RESP_BAD: *response = DRV_STORE_BAD; break; + case RESP_NO: *response = DRV_MSG_BAD; break; + default: *response = DRV_OK; break; } } +static void +imap_done_simple_msg( imap_store_t *ctx ATTR_UNUSED, + struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_simple *cmdp = (struct imap_cmd_simple *)cmd; + + transform_msg_response( &response ); + cmdp->callback( response, cmdp->callback_aux ); +} + /* static void drain_imap_replies( imap_store_t *ctx ) @@ -837,7 +902,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) @@ -869,7 +934,6 @@ parse_capability( imap_store_t *ctx, char *cmd ) for (i = 0; i < as(cap_list); i++) if (!strcmp( cap_list[i], arg )) ctx->caps |= 1 << i; - ctx->rcaps = ctx->caps; } static int @@ -906,10 +970,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_store_msg *)cmd)->out_uid = atoi( arg ))) { error( "IMAP error: malformed APPENDUID status\n" ); return RESP_BAD; @@ -941,7 +1006,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_find_msg *)cmdp)->out_uid = uid; return; } error( "IMAP error: unexpected SEARCH response (UID %u)\n", uid ); @@ -976,8 +1041,8 @@ parse_list_rsp( imap_store_t *ctx, char *cmd ) static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) { - struct imap_cmd *cmdp, **pcmdp, *ncmdp; - char *cmd, *arg, *arg1, *p; + struct imap_cmd *cmdp, **pcmdp; + char *cmd, *arg, *arg1; int n, resp, resp2, tag; for (;;) { @@ -1028,6 +1093,14 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) cmdp = (struct imap_cmd *)((char *)ctx->in_progress_append - offsetof(struct imap_cmd, next)); if (cmdp->param.data) { + /* If we get here, we will not get NO [TRYCREATE] any more. + * This code assumes that only STORE provides direct data. */ + if (cmdp->param.to_trash) + ctx->trashnc = 0; +#if 0 + else + ctx->currentnc = 0; +#endif n = socket_write( &ctx->buf.sock, cmdp->param.data, cmdp->param.data_len ); free( cmdp->param.data ); cmdp->param.data = 0; @@ -1064,28 +1137,12 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) resp = DRV_OK; else { if (!strcmp( "NO", arg )) { - if (cmdp->param.create && cmd && (cmdp->param.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */ - 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. */ - ncmdp = nfmalloc( sizeof(*ncmdp) ); - memcpy( &ncmdp->param, &cmdp->param, sizeof(cmdp->param) ); - ncmdp->param.create = 0; - if (!submit_imap_cmd( ctx, ncmdp, "%s", cmdp->cmd )) { - resp = RESP_BAD; - goto normal; - } - free( cmdp->cmd ); - free( cmdp ); - if (!tcmd) - return 0; /* ignored */ - if (cmdp == tcmd) - tcmd = ncmdp; - continue; + if (cmdp->param.create && + (cmdp->param.trycreate || + (cmd && !memcmp( cmd, "[TRYCREATE]", 11 )))) + { /* SELECT, APPEND or UID COPY */ + resp = RESP_TRYCREATE; + goto normal; } resp = RESP_NO; } else /*if (!strcmp( "BAD", arg ))*/ @@ -1101,7 +1158,8 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd ) cmdp->param.done( ctx, cmdp, resp ); if (cmdp->param.data) free( cmdp->param.data ); - free( cmdp->cmd ); + if (cmdp->cmd) /* "done" might transfer ownership */ + free( cmdp->cmd ); free( cmdp ); if (!tcmd || tcmd == cmdp) return resp; @@ -1154,14 +1212,21 @@ imap_own_store( store_conf_t *conf ) } static void +imap_cleanup_p2( imap_store_t *ctx, + struct imap_cmd *cmd ATTR_UNUSED, int response ATTR_UNUSED ) +{ + imap_cancel_store( &ctx->gen ); +} + +static void imap_cleanup( void ) { store_t *ctx, *nctx; for (ctx = unowned; ctx; ctx = nctx) { nctx = ctx->next; - imap_exec( (imap_store_t *)ctx, 0, "LOGOUT" ); - imap_cancel_store( ctx ); + imap_run( (imap_store_t *)ctx, new_imap_cmd( sizeof(struct imap_cmd) ), + imap_cleanup_p2, "LOGOUT" ); } } @@ -1446,7 +1511,7 @@ imap_open_store( store_conf_t *conf, } #if HAVE_LIBSSL if (CAP(CRAM)) { - struct imap_cmd *cmd = new_imap_cmd(); + struct imap_cmd *cmd = new_imap_cmd( sizeof(*cmd) ); info( "Authenticating with CRAM-MD5\n" ); cmd->param.cont = do_cram_auth; @@ -1514,16 +1579,160 @@ imap_prepare_opts( store_t *gctx, int opts ) gctx->opts = opts; } +static void +imap_select_p4( imap_store_t *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_simple *cmdp = (struct imap_cmd_simple *)cmd; + + transform_box_response( &response ); + if (response != DRV_OK) { + cmdp->callback( response, cmdp->callback_aux ); + } else { + struct imap_cmd_simple *cmd2; + char *p; + + INIT_IMAP_CMD(imap_cmd_simple, cmd2, + cmdp->callback, cmdp->callback_aux) + p = strchr( cmd->cmd, '"' ); + imap_run( ctx, &cmd2->gen, imap_done_simple_box, + "SELECT %.*s", strchr( p + 1, '"' ) - p + 1, p ); + } +} + +struct imap_select2_state { + int (*callback)( int sts, void *aux ); + void *callback_aux; + int ref_count; + int ret_val; +}; + +struct imap_cmd_select2 { + struct imap_cmd gen; + struct imap_select2_state *state; +}; + +static void +imap_select_bail( struct imap_select2_state *sts ) +{ + sts->callback( sts->ret_val, sts->callback_aux ); + free( sts ); +} + +static void +imap_select_p3( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int response ) +{ + struct imap_select2_state *sts = ((struct imap_cmd_select2 *)cmd)->state; + + switch (response) { + case RESP_BAD: + sts->ret_val = DRV_STORE_BAD; + break; + case RESP_NO: + if (sts->ret_val != DRV_STORE_BAD) + sts->ret_val = DRV_BOX_BAD; + break; + } + if (!--sts->ref_count) + imap_select_bail( sts ); +} + +static int +imap_submit_select2( imap_store_t *ctx, struct imap_select2_state *sts, + const char *buf, struct imap_cmd_select2 **cmd2p ) +{ + struct imap_cmd_select2 *cmd2 = + (struct imap_cmd_select2 *)new_imap_cmd( sizeof(*cmd2) ); + cmd2->gen.param.done = imap_select_p3; + cmd2->state = sts; + sts->ref_count++; + if (submit_imap_cmd( ctx, &cmd2->gen, + "UID FETCH %s (UID%s%s)", buf, + (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "", + (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "" )) + { + *cmd2p = cmd2; + return 1; + } + sts->ref_count--; + return 0; +} + +struct imap_cmd_select { + struct imap_cmd_simple gen; + int minuid, maxuid, *excs, nexcs; +}; + +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]; + + if (response == RESP_TRYCREATE) { + struct imap_cmd_simple *cmd2; + char *p; + + if (cmdp->excs) + free( cmdp->excs ); + INIT_IMAP_CMD(imap_cmd_simple, cmd2, + cmdp->gen.callback, cmdp->gen.callback_aux) + p = strchr( cmd->cmd, '"' ); + imap_run( ctx, &cmd2->gen, imap_select_p4, + "CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p ); + } else { + transform_box_response( &response ); + if (response != DRV_OK || !ctx->gen.count) { + if (cmdp->excs) + free( cmdp->excs ); + cmdp->gen.callback( response, cmdp->gen.callback_aux ); + } else { + struct imap_cmd_select2 *cmd2 = 0; + struct imap_select2_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->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", cmdp->excs[i] ); + j = i; + for (; i + 1 < cmdp->nexcs && cmdp->excs[i + 1] == cmdp->excs[i] + 1; i++); + if (i != j) + bl += sprintf( buf + bl, ":%d", cmdp->excs[i] ); + } + if (!imap_submit_select2( ctx, sts, buf, &cmd2 )) + goto bail; + } + if (cmdp->maxuid == INT_MAX) + cmdp->maxuid = ctx->uidnext ? ctx->uidnext - 1 : 1000000000; + if (cmdp->maxuid >= cmdp->minuid) { + sprintf( buf, "%d:%d", cmdp->minuid, cmdp->maxuid ); + imap_submit_select2( ctx, sts, buf, &cmd2 ); + } + bail: + if (cmdp->excs) + free( cmdp->excs ); + if (!--sts->ref_count) + imap_select_bail( sts ); + else + get_cmd_result( ctx, &cmd2->gen ); + } + } +} + static int imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, int (*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" )) { // ctx->currentnc = 0; @@ -1533,55 +1742,31 @@ imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, prefix = ctx->prefix; } - 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; - - if (gctx->count) { - ctx->msgapp = &gctx->msgs; - sort_ints( excs, nexcs ); - for (i = 0; i < nexcs; ) { - for (bl = 0; i < nexcs && bl < 960; i++) { - if (bl) - buf[bl++] = ','; - bl += sprintf( buf + bl, "%d", excs[i] ); - j = i; - for (; i + 1 < nexcs && excs[i + 1] == excs[i] + 1; i++); - if (i != j) - bl += sprintf( buf + bl, ":%d", excs[i] ); - } - 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 ? 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; - } - - ret = DRV_OK; - - bail: - if (excs) - free( excs ); - return cb( ret, aux ); + 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_run( ctx, &cmd->gen.gen, imap_select_p2, + "SELECT \"%s%s\"", prefix, gctx->name ); + return 0; } static int imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data, int (*cb)( int sts, void *aux ), void *aux ) { - struct imap_cmd *cmd = new_imap_cmd(); - cmd->param.uid = msg->uid; - cmd->param.aux = data; - return 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_run( (imap_store_t *)ctx, &cmd->gen.gen, imap_done_simple_msg, + "UID FETCH %d (%sBODY.PEEK[])", + msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " ); + return 0; } static int @@ -1638,7 +1823,56 @@ static int imap_close( store_t *ctx, int (*cb)( int sts, void *aux ), void *aux ) { - return 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_run( (imap_store_t *)ctx, &cmd->gen, imap_done_simple_box, "CLOSE" ); + return 0; +} + +struct imap_cmd_trash_msg { + struct imap_cmd_simple gen; + char *orig_cmd; +}; + +static void +imap_trash_msg_p3( imap_store_t *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_trash_msg *cmdp = (struct imap_cmd_trash_msg *)cmd; + + transform_msg_response( &response ); /* XXX no code to indicate trash failure */ + if (response != DRV_OK) { + cmdp->gen.callback( response, cmdp->gen.callback_aux ); + } else { + struct imap_cmd_simple *cmd2; + INIT_IMAP_CMD(imap_cmd_simple, cmd2, + cmdp->gen.callback, cmdp->gen.callback_aux) + imap_run( ctx, &cmd2->gen, imap_done_simple_msg, + "%s", cmdp->orig_cmd ); + } + free( cmdp->orig_cmd ); +} + +static void +imap_trash_msg_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_simple *cmdp = (struct imap_cmd_simple *)cmd; + + if (response != RESP_TRYCREATE) { + transform_msg_response( &response ); + cmdp->callback( response, cmdp->callback_aux ); + } else { + struct imap_cmd_trash_msg *cmd2; + char *p; + + INIT_IMAP_CMD_X(imap_cmd_trash_msg, cmd2, + cmdp->callback, cmdp->callback_aux) + cmd2->orig_cmd = cmd->cmd; + cmd->cmd = 0; + p = strchr( cmd2->orig_cmd, '"' ); + imap_run( ctx, &cmd2->gen.gen, imap_trash_msg_p3, + "CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p ); + } } static int @@ -1646,10 +1880,68 @@ imap_trash_msg( store_t *gctx, message_t *msg, int (*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; - return 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; + imap_run( ctx, &cmd->gen, imap_trash_msg_p2, + "UID COPY %d \"%s%s\"", + msg->uid, ctx->prefix, gctx->conf->trash ); + return 0; +} + +static void +imap_store_msg_p4( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_store_msg *cmdp = (struct imap_cmd_store_msg *)cmd; + + transform_msg_response( &response ); + cmdp->callback( response, cmdp->out_uid, cmdp->callback_aux ); +} + +struct imap_cmd_store_msg2 { + struct imap_cmd_store_msg gen; + char *orig_cmd; +}; + +static void +imap_store_msg_p3( imap_store_t *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_store_msg2 *cmdp = (struct imap_cmd_store_msg2 *)cmd; + + transform_msg_response( &response ); + if (response != DRV_OK) { + cmdp->gen.callback( response, -1, cmdp->gen.callback_aux ); + } else { + struct imap_cmd_store_msg *cmd2; + INIT_IMAP_CMD(imap_cmd_store_msg, cmd2, + cmdp->gen.callback, cmdp->gen.callback_aux) + imap_run( ctx, &cmd2->gen, imap_store_msg_p4, + "%s", cmdp->orig_cmd ); + } + free( cmdp->orig_cmd ); +} + +static void +imap_store_msg_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_store_msg *cmdp = (struct imap_cmd_store_msg *)cmd; + + if (response != RESP_TRYCREATE) { + transform_msg_response( &response ); + cmdp->callback( response, cmdp->out_uid, cmdp->callback_aux ); + } else { + struct imap_cmd_store_msg2 *cmd2; + char *p; + + INIT_IMAP_CMD_X(imap_cmd_store_msg2, cmd2, + cmdp->callback, cmdp->callback_aux) + cmd2->orig_cmd = cmd->cmd; + cmd->cmd = 0; + p = strchr( cmd2->orig_cmd, '"' ); + imap_run( ctx, &cmd2->gen.gen, imap_store_msg_p3, + "CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p ); + } } static int @@ -1657,9 +1949,9 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, int (*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_store_msg *cmd; const char *prefix, *box; - int ret, d, uid; + int d; char flagstr[128]; d = 0; @@ -1669,35 +1961,41 @@ 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_store_msg, 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; - if (ctx->trashnc) - ctx->caps = ctx->rcaps & ~(1 << LITERALPLUS); + cmd->gen.param.create = 1; + cmd->gen.param.to_trash = 1; } else { box = gctx->name; prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix; - cmd->param.create = (gctx->opts & OPEN_CREATE) != 0; - /*if (ctx->currentnc) - ctx->caps = ctx->rcaps & ~(1 << LITERALPLUS);*/ - } - ret = imap_exec_m( ctx, cmd, "APPEND \"%s%s\" %s", prefix, box, flagstr ); - ctx->caps = ctx->rcaps; - if (ret != DRV_OK) - return cb( ret, -1, aux ); - if (to_trash) - ctx->trashnc = 0; - else { - /*ctx->currentnc = 0;*/ +#if 0 + /* This is useless as we always SELECT the mailbox first + * (to obtain the UIDVALIDITY upfront). */ + cmd->gen.param.create = (gctx->opts & OPEN_CREATE) != 0; +#endif } + imap_run( ctx, &cmd->gen, imap_store_msg_p2, + "APPEND \"%s%s\" %s", prefix, box, flagstr ); + return 0; +} + +static void +imap_find_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_find_msg *cmdp = (struct imap_cmd_find_msg *)cmd; - return cb( DRV_OK, uid, aux ); + transform_msg_response( &response ); + if (response != DRV_OK) + cmdp->callback( response, -1, cmdp->callback_aux ); + else + cmdp->callback( cmdp->out_uid <= 0 ? DRV_MSG_BAD : DRV_OK, + cmdp->out_uid, cmdp->callback_aux ); } static int @@ -1705,16 +2003,31 @@ imap_find_msg( store_t *gctx, const char *tuid, int (*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) - return cb( ret, -1, aux ); - else - return cb( uid <= 0 ? DRV_MSG_BAD : DRV_OK, uid, aux ); + struct imap_cmd_find_msg *cmd; + + INIT_IMAP_CMD(imap_cmd_find_msg, 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_run( ctx, &cmd->gen, imap_find_msg_p2, + "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid ); + return 0; +} + +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 *ctx, struct imap_cmd *cmd, int response ) +{ + struct imap_cmd_list *cmdp = (struct imap_cmd_list *)cmd; + + transform_box_response( &response ); + if (response == DRV_OK) + ctx->gen.listed = 1; + cmdp->callback( response, cmdp->callback_aux ); } static void @@ -1722,11 +2035,11 @@ 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; - if ((ret = imap_exec_b( ctx, 0, "LIST \"\" \"%s%%\"", ctx->prefix )) == DRV_OK) - gctx->listed = 1; - cb( ret, aux ); + INIT_IMAP_CMD(imap_cmd_list, cmd, cb, aux) + imap_run( ctx, &cmd->gen, imap_list_p2, + "LIST \"\" \"%s%%\"", ctx->prefix ); } static void ------------------------------------------------------------------------------ The Planet: dedicated and managed hosting, cloud storage, colocation Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away. http://p.sf.net/sfu/theplanet-com _______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel