isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit ea951a697f467bfe97fc244278e72c44aac88e60
parent ec8f440383d997e11d9374c6862bf1df81f203d3
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Sun, 29 Jul 2012 23:14:48 +0200

fix error paths wrt sync drivers, take 2

synchronous error codes which are passed through callbacks aren't a
particularly good idea, after all: latest when the callback does stuff
which does not concern the caller, the return code becomes ambiguous.
instead, protect the sync_vars object with a refcount when invoking
driver functions from loops, as the callbacks they call could invalidate
the object and we would have no way of knowing that the loop should be
aborted prematurely. the upcoming async imap driver will also need a
refcount to protect the cancelation marker of the imap socket dispatcher
loop.

Diffstat:
Msrc/drv_imap.c | 55+++++++++++++++++++++++++++----------------------------
Msrc/drv_maildir.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/isync.h | 28++++++++++++++--------------
Msrc/sync.c | 226++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
4 files changed, 270 insertions(+), 187 deletions(-)

diff --git a/src/drv_imap.c b/src/drv_imap.c @@ -1554,9 +1554,9 @@ imap_prepare_opts( store_t *gctx, int opts ) gctx->opts = opts; } -static int +static void imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; struct imap_cmd *cmd = new_imap_cmd(); @@ -1609,18 +1609,18 @@ imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, bail: free( excs ); - return cb( ret, aux ); + cb( ret, aux ); } -static int +static void imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data, - int (*cb)( int sts, void *aux ), void *aux ) + void (*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 ); + cb( imap_exec_m( (imap_store_t *)ctx, cmd, "UID FETCH %d (%sBODY.PEEK[])", + msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " ), aux ); } static int @@ -1652,9 +1652,9 @@ imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags) return process_imap_replies( ctx ) == RESP_CANCEL ? DRV_CANCELED : DRV_OK; } -static int +static void imap_set_flags( store_t *gctx, message_t *msg, int uid, int add, int del, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; int ret; @@ -1669,31 +1669,31 @@ imap_set_flags( store_t *gctx, message_t *msg, int uid, int add, int 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; - return cb( ret, aux ); + cb( ret, aux ); } -static int +static void imap_close( store_t *ctx, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { - return cb( imap_exec_b( (imap_store_t *)ctx, 0, "CLOSE" ), aux ); + cb( imap_exec_b( (imap_store_t *)ctx, 0, "CLOSE" ), aux ); } -static int +static void imap_trash_msg( store_t *gctx, message_t *msg, - int (*cb)( int sts, void *aux ), void *aux ) + 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; - return cb( imap_exec_m( ctx, cmd, "UID COPY %d \"%s%s\"", - msg->uid, ctx->prefix, gctx->conf->trash ), aux ); + cb( imap_exec_m( ctx, cmd, "UID COPY %d \"%s%s\"", + msg->uid, ctx->prefix, gctx->conf->trash ), aux ); } -static int +static void imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, - int (*cb)( int sts, int uid, void *aux ), void *aux ) + 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(); @@ -1722,16 +1722,15 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, 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) - return cb( ret, -1, aux ); - - return cb( DRV_OK, uid, aux ); + if ((ret = imap_exec_m( ctx, cmd, "APPEND \"%s%s\" %s", prefix, box, flagstr )) != DRV_OK) + cb( ret, -1, aux ); + else + cb( DRV_OK, uid, aux ); } -static int +static void imap_find_msg( store_t *gctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ) + 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(); @@ -1741,9 +1740,9 @@ imap_find_msg( store_t *gctx, const char *tuid, 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 ); + cb( ret, -1, aux ); else - return cb( uid <= 0 ? DRV_MSG_BAD : DRV_OK, uid, aux ); + cb( uid <= 0 ? DRV_MSG_BAD : DRV_OK, uid, aux ); } static void diff --git a/src/drv_maildir.c b/src/drv_maildir.c @@ -771,9 +771,9 @@ maildir_prepare_opts( store_t *gctx, int opts ) gctx->opts = opts; } -static int +static void maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; message_t **msgapp; @@ -789,14 +789,17 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, ctx->excs = nfrealloc( excs, nexcs * sizeof(int) ); ctx->nexcs = nexcs; - if ((ret = maildir_validate( gctx->path, "", ctx->gen.opts & OPEN_CREATE, ctx )) != DRV_OK) - return cb( ret, aux ); + if ((ret = maildir_validate( gctx->path, "", ctx->gen.opts & OPEN_CREATE, ctx )) != DRV_OK) { + cb( ret, aux ); + return; + } nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", gctx->path ); #ifndef USE_DB if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) { perror( uvpath ); - return cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, aux ); + return; } #else if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) { @@ -811,7 +814,8 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, goto fnok; } perror( uvpath ); - return cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, aux ); + return; } dbok: #if SEEK_SET != 0 @@ -823,7 +827,8 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, bork: close( ctx->uvfd ); ctx->uvfd = -1; - return cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, aux ); + return; } if (db_create( &ctx->db, 0, 0 )) { fputs( "Maildir error: db_create() failed\n", stderr ); @@ -853,14 +858,16 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, fnok: #endif /* USE_DB */ - if (maildir_scan( ctx, &msglist ) != DRV_OK) - return cb( DRV_BOX_BAD, aux ); + if (maildir_scan( ctx, &msglist ) != DRV_OK) { + cb( DRV_BOX_BAD, aux ); + return; + } msgapp = &ctx->gen.msgs; for (i = 0; i < msglist.nents; i++) maildir_app_msg( ctx, &msgapp, msglist.ents + i ); maildir_free_scan( &msglist ); - return cb( DRV_OK, aux ); + cb( DRV_OK, aux ); } static int @@ -928,9 +935,9 @@ maildir_again( maildir_store_t *ctx, maildir_message_t *msg, const char *fn ) return (msg->gen.status & M_DEAD) ? DRV_MSG_BAD : DRV_OK; } -static int +static void maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg = (maildir_message_t *)gmsg; @@ -942,8 +949,10 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, nfsnprintf( buf, sizeof(buf), "%s/%s/%s", gctx->path, subdirs[gmsg->status & M_RECENT], msg->base ); if ((fd = open( buf, O_RDONLY )) >= 0) break; - if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) - return cb( ret, aux ); + if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { + cb( ret, aux ); + return; + } } fstat( fd, &st ); data->len = st.st_size; @@ -951,12 +960,13 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, if (read( fd, data->data, data->len ) != data->len) { perror( buf ); close( fd ); - return cb( DRV_MSG_BAD, aux ); + cb( DRV_MSG_BAD, aux ); + return; } close( fd ); if (!(gmsg->status & M_FLAGS)) data->flags = maildir_parse_flags( msg->base ); - return cb( DRV_OK, aux ); + cb( DRV_OK, aux ); } static int @@ -974,9 +984,9 @@ maildir_make_flags( int flags, char *buf ) return d; } -static int +static void maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, - int (*cb)( int sts, int uid, void *aux ), void *aux ) + void (*cb)( int sts, int uid, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; const char *prefix, *box; @@ -989,14 +999,17 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, if (ctx->db) { if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) { free( data->data ); - return cb( ret, 0, aux ); + cb( ret, 0, aux ); + return; } } else #endif /* USE_DB */ { if ((ret = maildir_uidval_lock( ctx )) != DRV_OK || - (ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) - return cb( ret, 0, aux ); + (ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) { + cb( ret, 0, aux ); + return; + } maildir_uidval_unlock( ctx ); nfsnprintf( base + bl, sizeof(base) - bl, ",U=%d", uid ); } @@ -1013,16 +1026,19 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, if (errno != ENOENT) { perror( buf ); free( data->data ); - return cb( DRV_BOX_BAD, 0, aux ); + cb( DRV_BOX_BAD, 0, aux ); + return; } if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, gctx->opts & OPEN_CREATE, ctx )) != DRV_OK) { free( data->data ); - return cb( ret, 0, aux ); + cb( ret, 0, aux ); + return; } if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) { perror( buf ); free( data->data ); - return cb( DRV_BOX_BAD, 0, aux ); + cb( DRV_BOX_BAD, 0, aux ); + return; } } ret = write( fd, data->data, data->len ); @@ -1033,38 +1049,43 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, else error( "Maildir error: %s: partial write\n", buf ); close( fd ); - return cb( DRV_BOX_BAD, 0, aux ); + cb( DRV_BOX_BAD, 0, aux ); + return; } if (close( fd ) < 0) { /* Quota exceeded may cause this. */ perror( buf ); - return cb( DRV_BOX_BAD, 0, aux ); + cb( DRV_BOX_BAD, 0, aux ); + return; } /* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */ nfsnprintf( nbuf, sizeof(nbuf), "%s%s/%s/%s%s", prefix, box, subdirs[!(data->flags & F_SEEN)], base, fbuf ); if (rename( buf, nbuf )) { perror( nbuf ); - return cb( DRV_BOX_BAD, 0, aux ); + cb( DRV_BOX_BAD, 0, aux ); + return; } - return cb( DRV_OK, uid, aux ); + cb( DRV_OK, uid, aux ); } -static int +static void maildir_find_msg( store_t *gctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ) + void (*cb)( int sts, int uid, void *aux ), void *aux ) { message_t *msg; /* using a hash table might turn out to be more appropriate ... */ for (msg = gctx->msgs; msg; msg = msg->next) - if (!(msg->status & M_DEAD) && !memcmp( ((maildir_message_t *)msg)->tuid, tuid, TUIDL )) - return cb( DRV_OK, msg->uid, aux ); - return cb( DRV_MSG_BAD, -1, aux ); + if (!(msg->status & M_DEAD) && !memcmp( ((maildir_message_t *)msg)->tuid, tuid, TUIDL )) { + cb( DRV_OK, msg->uid, aux ); + return; + } + cb( DRV_MSG_BAD, -1, aux ); } -static int +static void maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg = (maildir_message_t *)gmsg; @@ -1106,8 +1127,10 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del, } if (!rename( buf, nbuf )) break; - if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) - return cb( ret, aux ); + if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { + cb( ret, aux ); + return; + } } free( msg->base ); msg->base = nfmalloc( tl + 1 ); @@ -1116,7 +1139,7 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del, msg->gen.flags &= ~del; gmsg->status &= ~M_RECENT; - return cb( DRV_OK, aux ); + cb( DRV_OK, aux ); } #ifdef USE_DB @@ -1136,9 +1159,9 @@ maildir_purge_msg( maildir_store_t *ctx, const char *name ) } #endif /* USE_DB */ -static int +static void maildir_trash_msg( store_t *gctx, message_t *gmsg, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_message_t *msg = (maildir_message_t *)gmsg; @@ -1155,31 +1178,38 @@ maildir_trash_msg( store_t *gctx, message_t *gmsg, if (!rename( buf, nbuf )) break; if (!stat( buf, &st )) { - if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, 1, ctx )) != DRV_OK) - return cb( ret, aux ); + if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, 1, ctx )) != DRV_OK) { + cb( ret, aux ); + return; + } if (!rename( buf, nbuf )) break; if (errno != ENOENT) { perror( nbuf ); - return cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, aux ); + return; } } - if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) - return cb( ret, aux ); + if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { + cb( ret, aux ); + return; + } } gmsg->status |= M_DEAD; gctx->count--; #ifdef USE_DB - if (ctx->db) - return cb( maildir_purge_msg( ctx, msg->base ), aux ); + if (ctx->db) { + cb( maildir_purge_msg( ctx, msg->base ), aux ); + return; + } #endif /* USE_DB */ - return cb( DRV_OK, aux ); + cb( DRV_OK, aux ); } -static int +static void maildir_close( store_t *gctx, - int (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, void *aux ), void *aux ) { #ifdef USE_DB maildir_store_t *ctx = (maildir_store_t *)gctx; @@ -1203,15 +1233,21 @@ maildir_close( store_t *gctx, msg->status |= M_DEAD; gctx->count--; #ifdef USE_DB - if (ctx->db && (ret = maildir_purge_msg( ctx, ((maildir_message_t *)msg)->base )) != DRV_OK) - return cb( ret, aux ); + if (ctx->db && (ret = maildir_purge_msg( ctx, ((maildir_message_t *)msg)->base )) != DRV_OK) { + cb( ret, aux ); + return; + } #endif /* USE_DB */ } } - if (!retry) - return cb( DRV_OK, aux ); - if ((ret = maildir_rescan( (maildir_store_t *)gctx )) != DRV_OK) - return cb( ret, aux ); + if (!retry) { + cb( DRV_OK, aux ); + return; + } + if ((ret = maildir_rescan( (maildir_store_t *)gctx )) != DRV_OK) { + cb( ret, aux ); + return; + } } } diff --git a/src/isync.h b/src/isync.h @@ -210,20 +210,20 @@ struct driver { void (*cb)( int sts, void *aux ), void *aux ); void (*prepare_paths)( store_t *ctx ); void (*prepare_opts)( store_t *ctx, int opts ); - int (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs, - int (*cb)( int sts, void *aux ), void *aux ); - int (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data, - int (*cb)( int sts, void *aux ), void *aux ); - int (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash, - int (*cb)( int sts, int uid, void *aux ), void *aux ); - int (*find_msg)( store_t *ctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ); - int (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del, /* msg can be null, therefore uid as a fallback */ - int (*cb)( int sts, void *aux ), void *aux ); - int (*trash_msg)( store_t *ctx, message_t *msg, /* This may expunge the original message immediately, but it needn't to */ - int (*cb)( int sts, void *aux ), void *aux ); - int (*close)( store_t *ctx, /* IMAP-style: expunge inclusive */ - int (*cb)( int sts, void *aux ), void *aux ); + void (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs, + void (*cb)( int sts, void *aux ), void *aux ); + void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data, + void (*cb)( int sts, void *aux ), void *aux ); + void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash, + void (*cb)( int sts, int uid, void *aux ), void *aux ); + void (*find_msg)( store_t *ctx, const char *tuid, + void (*cb)( int sts, int uid, void *aux ), void *aux ); + void (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del, /* msg can be null, therefore uid as a fallback */ + void (*cb)( int sts, void *aux ), void *aux ); + void (*trash_msg)( store_t *ctx, message_t *msg, /* This may expunge the original message immediately, but it needn't to */ + void (*cb)( int sts, void *aux ), void *aux ); + void (*close)( store_t *ctx, /* IMAP-style: expunge inclusive */ + void (*cb)( int sts, void *aux ), void *aux ); void (*cancel)( store_t *ctx, /* only not yet sent commands */ void (*cb)( void *aux ), void *aux ); void (*commit)( store_t *ctx ); diff --git a/src/sync.c b/src/sync.c @@ -145,7 +145,7 @@ typedef struct { channel_conf_t *chan; store_t *ctx[2]; driver_t *drv[2]; - int state[2], ret; + int state[2], ref_count, ret; int find_old_total[2], find_old_done[2]; int new_total[2], new_done[2]; int find_new_total[2], find_new_done[2]; @@ -155,6 +155,26 @@ typedef struct { unsigned find:1; } sync_vars_t; +static void sync_ref( sync_vars_t *svars ) { ++svars->ref_count; } +static int sync_deref( sync_vars_t *svars ); +static int deref_check_cancel( sync_vars_t *svars ); +static int check_cancel( sync_vars_t *svars ); + +#define DRIVER_CALL_RET(call) \ + do { \ + sync_ref( svars ); \ + svars->drv[t]->call; \ + return deref_check_cancel( svars ); \ + } while (0) + +#define DRIVER_CALL(call) \ + do { \ + sync_ref( svars ); \ + svars->drv[t]->call; \ + if (deref_check_cancel( svars )) \ + return; \ + } while (0) + #define AUX &svars->t[t] #define DECL_SVARS \ int t; \ @@ -192,27 +212,28 @@ typedef struct { typedef struct copy_vars { - int (*cb)( int sts, int uid, struct copy_vars *vars ); + void (*cb)( int sts, int uid, struct copy_vars *vars ); void *aux; sync_rec_t *srec; /* also ->tuid */ message_t *msg; msg_data_t data; } copy_vars_t; -static int msg_fetched( int sts, void *aux ); +static void msg_fetched( int sts, void *aux ); static int copy_msg( copy_vars_t *vars ) { DECL_INIT_SVARS(vars->aux); + t ^= 1; vars->data.flags = vars->msg->flags; - return svars->drv[1-t]->fetch_msg( svars->ctx[1-t], vars->msg, &vars->data, msg_fetched, vars ); + DRIVER_CALL_RET(fetch_msg( svars->ctx[t], vars->msg, &vars->data, msg_fetched, vars )); } -static int msg_stored( int sts, int uid, void *aux ); +static void msg_stored( int sts, int uid, void *aux ); -static int +static void msg_fetched( int sts, void *aux ) { copy_vars_t *vars = (copy_vars_t *)aux; @@ -260,7 +281,8 @@ msg_fetched( int sts, void *aux ) warn( "Warning: message %d from %s has incomplete header.\n", vars->msg->uid, str_ms[1-t] ); free( fmap ); - return vars->cb( SYNC_NOGOOD, 0, vars ); + vars->cb( SYNC_NOGOOD, 0, vars ); + return; oke: extra += 8 + TUIDL + 1 + (tcr && (!scr || hcrs)); } @@ -327,17 +349,21 @@ msg_fetched( int sts, void *aux ) free( fmap ); } - return svars->drv[t]->store_msg( svars->ctx[t], &vars->data, !vars->srec, msg_stored, vars ); + svars->drv[t]->store_msg( svars->ctx[t], &vars->data, !vars->srec, msg_stored, vars ); + break; case DRV_CANCELED: - return vars->cb( SYNC_CANCELED, 0, vars ); + vars->cb( SYNC_CANCELED, 0, vars ); + break; case DRV_MSG_BAD: - return vars->cb( SYNC_NOGOOD, 0, vars ); + vars->cb( SYNC_NOGOOD, 0, vars ); + break; default: - return vars->cb( SYNC_FAIL, 0, vars ); + vars->cb( SYNC_FAIL, 0, vars ); + break; } } -static int +static void msg_stored( int sts, int uid, void *aux ) { copy_vars_t *vars = (copy_vars_t *)aux; @@ -345,17 +371,21 @@ msg_stored( int sts, int uid, void *aux ) switch (sts) { case DRV_OK: - return vars->cb( SYNC_OK, uid, vars ); + vars->cb( SYNC_OK, uid, vars ); + break; case DRV_CANCELED: - return vars->cb( SYNC_CANCELED, 0, vars ); + vars->cb( SYNC_CANCELED, 0, vars ); + break; case DRV_MSG_BAD: INIT_SVARS(vars->aux); (void)svars; warn( "Warning: %s refuses to store message %d from %s.\n", str_ms[t], vars->msg->uid, str_ms[1-t] ); - return vars->cb( SYNC_NOGOOD, 0, vars ); + vars->cb( SYNC_NOGOOD, 0, vars ); + break; default: - return vars->cb( SYNC_FAIL, 0, vars ); + vars->cb( SYNC_FAIL, 0, vars ); + break; } } @@ -433,6 +463,19 @@ store_bad( void *aux ) cancel_sync( svars ); } +static int +deref_check_cancel( sync_vars_t *svars ) +{ + if (sync_deref( svars )) + return -1; + return check_cancel( svars ); +} + +static int +check_cancel( sync_vars_t *svars ) +{ + return (svars->state[M] | svars->state[S]) & (ST_SENT_CANCEL | ST_CANCELED); +} static int check_ret( int sts, void *aux ) @@ -454,7 +497,7 @@ check_ret( int sts, void *aux ) #define SVARS_CHECK_RET \ DECL_SVARS; \ if (check_ret( sts, aux )) \ - return 1; \ + return; \ INIT_SVARS(aux) #define SVARS_CHECK_RET_VARS(type) \ @@ -462,7 +505,7 @@ check_ret( int sts, void *aux ) DECL_SVARS; \ if (check_ret( sts, vars->aux )) { \ free( vars ); \ - return 1; \ + return; \ } \ INIT_SVARS(vars->aux) @@ -470,7 +513,7 @@ check_ret( int sts, void *aux ) DECL_SVARS; \ if (sts == SYNC_CANCELED) { \ free( vars ); \ - return 1; \ + return; \ } \ INIT_SVARS(vars->aux) @@ -508,6 +551,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, svars = nfcalloc( sizeof(*svars) ); svars->t[1] = 1; + svars->ref_count = 1; svars->cb = cb; svars->aux = aux; svars->ctx[0] = ctx[0]; @@ -830,7 +874,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, select_box( svars, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 ); } -static int box_selected( int sts, void *aux ); +static void box_selected( int sts, void *aux ); static int select_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs ) @@ -851,7 +895,7 @@ select_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs ) maxwuid = 0; info( "Selecting %s %s...\n", str_ms[t], svars->ctx[t]->name ); debug( maxwuid == INT_MAX ? "selecting %s [%d,inf]\n" : "selecting %s [%d,%d]\n", str_ms[t], minwuid, maxwuid ); - return svars->drv[t]->select( svars->ctx[t], minwuid, maxwuid, mexcs, nmexcs, box_selected, AUX ); + DRIVER_CALL_RET(select( svars->ctx[t], minwuid, maxwuid, mexcs, nmexcs, box_selected, AUX )); } typedef struct { @@ -859,10 +903,10 @@ typedef struct { sync_rec_t *srec; } find_vars_t; -static int msg_found_sel( int sts, int uid, void *aux ); -static int msgs_found_sel( sync_vars_t *svars, int t ); +static void msg_found_sel( int sts, int uid, void *aux ); +static void msgs_found_sel( sync_vars_t *svars, int t ); -static int +static void box_selected( int sts, void *aux ) { find_vars_t *fv; @@ -874,7 +918,7 @@ box_selected( int sts, void *aux ) str_ms[t], svars->ctx[t]->uidvalidity, svars->uidval[t] ); svars->ret |= SYNC_FAIL; cancel_sync( svars ); - return 1; + return; } info( "%s: %d messages, %d recent\n", str_ms[t], svars->ctx[t]->count, svars->ctx[t]->recent ); @@ -897,16 +941,15 @@ box_selected( int sts, void *aux ) fv = nfmalloc( sizeof(*fv) ); fv->aux = AUX; fv->srec = srec; - if (svars->drv[t]->find_msg( svars->ctx[t], srec->tuid, msg_found_sel, fv )) - return 1; + DRIVER_CALL(find_msg( svars->ctx[t], srec->tuid, msg_found_sel, fv )); } } } svars->state[t] |= ST_SENT_FIND_OLD; - return msgs_found_sel( svars, t ); + msgs_found_sel( svars, t ); } -static int +static void msg_found_sel( int sts, int uid, void *aux ) { SVARS_CHECK_RET_VARS(find_vars_t); @@ -927,7 +970,7 @@ msg_found_sel( int sts, int uid, void *aux ) free( vars ); svars->find_old_done[t]++; stats( svars ); - return msgs_found_sel( svars, t ); + msgs_found_sel( svars, t ); } typedef struct { @@ -936,15 +979,15 @@ typedef struct { int aflags, dflags; } flag_vars_t; -static int flags_set_del( int sts, void *aux ); -static int flags_set_sync( int sts, void *aux ); +static void flags_set_del( int sts, void *aux ); +static void flags_set_sync( int sts, void *aux ); static void flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t ); static int msgs_flags_set( sync_vars_t *svars, int t ); -static int msg_copied( int sts, int uid, copy_vars_t *vars ); +static void msg_copied( int sts, int uid, copy_vars_t *vars ); static void msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int uid ); -static int msgs_copied( sync_vars_t *svars, int t ); +static void msgs_copied( sync_vars_t *svars, int t ); -static int +static void msgs_found_sel( sync_vars_t *svars, int t ) { sync_rec_t *srec, *nsrec = 0; @@ -957,7 +1000,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) char fbuf[16]; /* enlarge when support for keywords is added */ if (!(svars->state[t] & ST_SENT_FIND_OLD) || svars->find_old_done[t] < svars->find_old_total[t]) - return 0; + return; /* * Mapping tmsg -> srec (this variant) is dog slow for new messages. @@ -1053,11 +1096,12 @@ msgs_found_sel( sync_vars_t *svars, int t ) for (t = 0; t < nmexcs; t++) debugn( " %d", mexcs[t] ); debug( "\n" ); - return select_box( svars, M, minwuid, mexcs, nmexcs ); + select_box( svars, M, minwuid, mexcs, nmexcs ); + return; } if (!(svars->state[1-t] & ST_SENT_FIND_OLD) || svars->find_old_done[1-t] < svars->find_old_total[1-t]) - return 0; + return; if (svars->uidval[M] < 0 || svars->uidval[S] < 0) { svars->uidval[M] = svars->ctx[M]->uidvalidity; @@ -1113,7 +1157,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid ); debug( " -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid ); if (copy_msg( cv )) - return 1; + return; } else { if (tmsg->srec) { debug( " -> not %sing - still too big\n", str_hl[t] ); @@ -1125,8 +1169,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) } } svars->state[t] |= ST_SENT_NEW; - if (msgs_copied( svars, t )) - return 1; + msgs_copied( svars, t ); } debug( "synchronizing old entries\n" ); @@ -1164,8 +1207,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) fv = nfmalloc( sizeof(*fv) ); fv->aux = AUX; fv->srec = srec; - if (svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0, flags_set_del, fv )) - return 1; + DRIVER_CALL(set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0, flags_set_del, fv )); } else debug( " not %sing delete\n", str_hl[t] ); } else if (!srec->msg[1-t]) @@ -1275,8 +1317,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) fv->srec = srec; fv->aflags = aflags; fv->dflags = dflags; - if (svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags, flags_set_sync, fv )) - return 1; + DRIVER_CALL(set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags, flags_set_sync, fv )); } else flags_set_sync_p2( svars, srec, t ); } @@ -1285,12 +1326,11 @@ msgs_found_sel( sync_vars_t *svars, int t ) svars->drv[t]->commit( svars->ctx[t] ); svars->state[t] |= ST_SENT_FLAGS; if (msgs_flags_set( svars, t )) - return 1; + return; } - return 0; } -static int +static void msg_copied( int sts, int uid, copy_vars_t *vars ) { SVARS_CHECK_CANCEL_RET; @@ -1306,12 +1346,12 @@ msg_copied( int sts, int uid, copy_vars_t *vars ) default: cancel_sync( svars ); free( vars ); - return 1; + return; } free( vars ); svars->new_done[t]++; stats( svars ); - return msgs_copied( svars, t ); + msgs_copied( svars, t ); } static void @@ -1332,17 +1372,17 @@ msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int } } -static int msg_found_new( int sts, int uid, void *aux ); -static int sync_close( sync_vars_t *svars, int t ); +static void msg_found_new( int sts, int uid, void *aux ); +static void sync_close( sync_vars_t *svars, int t ); -static int +static void msgs_copied( sync_vars_t *svars, int t ) { sync_rec_t *srec; find_vars_t *fv; if (!(svars->state[t] & ST_SENT_NEW) || svars->new_done[t] < svars->new_total[t]) - return 0; + return; debug( "finding just copied messages on %s\n", str_ms[t] ); for (srec = svars->srecs; srec; srec = srec->next) { @@ -1355,15 +1395,14 @@ msgs_copied( sync_vars_t *svars, int t ) fv = nfmalloc( sizeof(*fv) ); fv->aux = AUX; fv->srec = srec; - if (svars->drv[t]->find_msg( svars->ctx[t], srec->tuid, msg_found_new, fv )) - return 1; + DRIVER_CALL(find_msg( svars->ctx[t], srec->tuid, msg_found_new, fv )); } } svars->state[t] |= ST_SENT_FIND_NEW; - return sync_close( svars, t ); + sync_close( svars, t ); } -static int +static void msg_found_new( int sts, int uid, void *aux ) { SVARS_CHECK_RET_VARS(find_vars_t); @@ -1382,10 +1421,10 @@ msg_found_new( int sts, int uid, void *aux ) free( vars ); svars->find_new_done[t]++; stats( svars ); - return sync_close( svars, t ); + sync_close( svars, t ); } -static int +static void flags_set_del( int sts, void *aux ) { SVARS_CHECK_RET_VARS(flag_vars_t); @@ -1399,10 +1438,10 @@ flags_set_del( int sts, void *aux ) free( vars ); svars->flags_done[t]++; stats( svars ); - return msgs_flags_set( svars, t ); + msgs_flags_set( svars, t ); } -static int +static void flags_set_sync( int sts, void *aux ) { SVARS_CHECK_RET_VARS(flag_vars_t); @@ -1418,7 +1457,7 @@ flags_set_sync( int sts, void *aux ) free( vars ); svars->flags_done[t]++; stats( svars ); - return msgs_flags_set( svars, t ); + msgs_flags_set( svars, t ); } static void @@ -1448,8 +1487,8 @@ flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t ) } } -static int msg_trashed( int sts, void *aux ); -static int msg_rtrashed( int sts, int uid, copy_vars_t *vars ); +static void msg_trashed( int sts, void *aux ); +static void msg_rtrashed( int sts, int uid, copy_vars_t *vars ); static int msgs_flags_set( sync_vars_t *svars, int t ) @@ -1470,8 +1509,10 @@ msgs_flags_set( sync_vars_t *svars, int t ) debug( "%s: trashing message %d\n", str_ms[t], tmsg->uid ); svars->trash_total[t]++; stats( svars ); - if (svars->drv[t]->trash_msg( svars->ctx[t], tmsg, msg_trashed, AUX )) - return 1; + sync_ref( svars ); + svars->drv[t]->trash_msg( svars->ctx[t], tmsg, msg_trashed, AUX ); + if (deref_check_cancel( svars )) + return -1; } else debug( "%s: not trashing message %d - not new\n", str_ms[t], tmsg->uid ); } else { @@ -1486,7 +1527,7 @@ msgs_flags_set( sync_vars_t *svars, int t ) cv->srec = 0; cv->msg = tmsg; if (copy_msg( cv )) - return 1; + return -1; } else debug( "%s: not remote trashing message %d - too big\n", str_ms[t], tmsg->uid ); } else @@ -1495,10 +1536,11 @@ msgs_flags_set( sync_vars_t *svars, int t ) } } svars->state[t] |= ST_SENT_TRASH; - return sync_close( svars, t ); + sync_close( svars, t ); + return 0; } -static int +static void msg_trashed( int sts, void *aux ) { DECL_SVARS; @@ -1506,14 +1548,14 @@ msg_trashed( int sts, void *aux ) if (sts == DRV_MSG_BAD) sts = DRV_BOX_BAD; if (check_ret( sts, aux )) - return 1; + return; INIT_SVARS(aux); svars->trash_done[t]++; stats( svars ); - return sync_close( svars, t ); + sync_close( svars, t ); } -static int +static void msg_rtrashed( int sts, int uid, copy_vars_t *vars ) { SVARS_CHECK_CANCEL_RET; @@ -1525,40 +1567,39 @@ msg_rtrashed( int sts, int uid, copy_vars_t *vars ) default: cancel_sync( svars ); free( vars ); - return 1; + return; } free( vars ); svars->trash_done[t]++; stats( svars ); - return sync_close( svars, t ); + sync_close( svars, t ); } -static int box_closed( int sts, void *aux ); +static void box_closed( int sts, void *aux ); static void box_closed_p2( sync_vars_t *svars, int t ); -static int +static void sync_close( sync_vars_t *svars, int t ) { if ((~svars->state[t] & (ST_SENT_FIND_NEW|ST_SENT_TRASH)) || svars->find_new_done[t] < svars->find_new_total[t] || svars->trash_done[t] < svars->trash_total[t]) - return 0; + return; if ((svars->chan->ops[t] & OP_EXPUNGE) /*&& !(svars->state[t] & ST_TRASH_BAD)*/) { debug( "expunging %s\n", str_ms[t] ); - return svars->drv[t]->close( svars->ctx[t], box_closed, AUX ); + svars->drv[t]->close( svars->ctx[t], box_closed, AUX ); + } else { + box_closed_p2( svars, t ); } - box_closed_p2( svars, t ); - return 0; } -static int +static void box_closed( int sts, void *aux ) { SVARS_CHECK_RET; svars->state[t] |= ST_DID_EXPUNGE; box_closed_p2( svars, t ); - return 0; } static void @@ -1655,16 +1696,23 @@ sync_bail1( sync_vars_t *svars ) static void sync_bail2( sync_vars_t *svars ) { - void (*cb)( int sts, void *aux ) = svars->cb; - void *aux = svars->aux; - int ret = svars->ret; - free( svars->lname ); free( svars->nname ); free( svars->jname ); free( svars->dname ); - free( svars ); error( "" ); - cb( ret, aux ); + sync_deref( svars ); } +static int sync_deref( sync_vars_t *svars ) +{ + if (!--svars->ref_count) { + void (*cb)( int sts, void *aux ) = svars->cb; + void *aux = svars->aux; + int ret = svars->ret; + free( svars ); + cb( ret, aux ); + return -1; + } + return 0; +}