diff --git a/src/driver.h b/src/driver.h index 8907621..caf29c4 100644 --- a/src/driver.h +++ b/src/driver.h @@ -88,9 +88,6 @@ typedef struct message { typedef struct store { struct store *next; store_conf_t *conf; /* foreign */ - - /* currently open mailbox */ - int uidvalidity; } store_t; typedef struct { @@ -174,7 +171,7 @@ struct driver { /* Open the selected mailbox. * Note that this should not directly complain about failure to open. */ void (*open_box)( store_t *ctx, - void (*cb)( int sts, void *aux ), void *aux ); + void (*cb)( int sts, int uidvalidity, void *aux ), void *aux ); /* Return the minimal UID the next stored message will have. */ int (*get_uidnext)( store_t *ctx ); diff --git a/src/drv_imap.c b/src/drv_imap.c index d6bd177..baba08d 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -112,7 +112,7 @@ struct imap_store { // note that the message counts do _not_ reflect stats from msgs, // but mailbox totals. also, don't trust them beyond the initial load. int total_msgs, recent_msgs; - int uidnext; + int uidvalidity, uidnext; message_t *msgs; message_t **msgapp; /* FETCH results */ uint caps; /* CAPABILITY results */ @@ -1166,7 +1166,7 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s ) goto bad_resp; if (!strcmp( "UIDVALIDITY", arg )) { if (!(arg = next_arg( &s )) || - (ctx->gen.uidvalidity = strtoll( arg, &earg, 10 ), *earg)) + (ctx->uidvalidity = strtoll( arg, &earg, 10 ), *earg)) { error( "IMAP error: malformed UIDVALIDITY status\n" ); return RESP_CANCEL; @@ -1186,7 +1186,7 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s ) error( "*** IMAP ALERT *** %s\n", p ); } else if (cmd && !strcmp( "APPENDUID", arg )) { if (!(arg = next_arg( &s )) || - (ctx->gen.uidvalidity = strtoll( arg, &earg, 10 ), *earg) || + (ctx->uidvalidity = strtoll( arg, &earg, 10 ), *earg) || !(arg = next_arg( &s )) || !(((imap_cmd_out_uid_t *)cmd)->out_uid = atoi( arg ))) { @@ -2340,25 +2340,33 @@ imap_get_box_path( store_t *gctx ATTR_UNUSED ) return 0; } +typedef struct { + imap_cmd_t gen; + void (*callback)( int sts, int uidvalidity, void *aux ); + void *callback_aux; +} imap_cmd_open_box_t; + static void imap_open_box_p2( imap_store_t *, imap_cmd_t *, int ); static void imap_open_box_p3( imap_store_t *, imap_cmd_t *, int ); +static void imap_open_box_p4( imap_store_t *, imap_cmd_open_box_t *, int ); static void imap_open_box( store_t *gctx, - void (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, int uidvalidity, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; - imap_cmd_simple_t *cmd; + imap_cmd_open_box_t *cmd; char *buf; if (prepare_box( &buf, ctx ) < 0) { - cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } + ctx->uidvalidity = UIDVAL_BAD; ctx->uidnext = 0; - INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux) + INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cb, aux) cmd->gen.param.failok = 1; imap_exec( ctx, &cmd->gen, imap_open_box_p2, "SELECT \"%\\s\"", buf ); @@ -2368,15 +2376,15 @@ imap_open_box( store_t *gctx, static void imap_open_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { - imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)gcmd; - imap_cmd_simple_t *cmd; + imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd; + imap_cmd_open_box_t *cmd; if (response != RESP_OK || ctx->uidnext) { - imap_done_simple_box( ctx, &cmdp->gen, response ); + imap_open_box_p4( ctx, cmdp, response ); return; } - INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cmdp->callback, cmdp->callback_aux) + INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cmdp->callback, cmdp->callback_aux) cmd->gen.param.lastuid = 1; imap_exec( ctx, &cmd->gen, imap_open_box_p3, "UID FETCH *:* (UID)" ); @@ -2385,11 +2393,20 @@ imap_open_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) static void imap_open_box_p3( imap_store_t *ctx, imap_cmd_t *gcmd, int response ) { + imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd; + // This will happen if the box is empty. if (!ctx->uidnext) ctx->uidnext = 1; - imap_done_simple_box( ctx, gcmd, response ); + imap_open_box_p4( ctx, cmdp, response ); +} + +static void +imap_open_box_p4( imap_store_t *ctx, imap_cmd_open_box_t *cmdp, int response ) +{ + transform_box_response( &response ); + cmdp->callback( response, ctx->uidvalidity, cmdp->callback_aux ); } static int diff --git a/src/drv_maildir.c b/src/drv_maildir.c index ef92987..2017c44 100644 --- a/src/drv_maildir.c +++ b/src/drv_maildir.c @@ -70,7 +70,7 @@ typedef struct { typedef struct { store_t gen; uint opts; - int uvfd, uvok, nuid, is_inbox, fresh[3]; + int uvfd, uvok, uidvalidity, nuid, is_inbox, fresh[3]; int minuid, maxuid, newuid, seenuid; int_array_t excs; char *path; /* own */ @@ -657,7 +657,7 @@ maildir_store_uidval( maildir_store_t *ctx ) if (ctx->db) { key.data = (void *)"UIDVALIDITY"; key.size = 11; - uv[0] = ctx->gen.uidvalidity; + uv[0] = ctx->uidvalidity; uv[1] = ctx->nuid; value.data = uv; value.size = sizeof(uv); @@ -672,7 +672,7 @@ maildir_store_uidval( maildir_store_t *ctx ) } else #endif /* USE_DB */ { - n = sprintf( buf, "%d\n%d\n", ctx->gen.uidvalidity, ctx->nuid ); + n = sprintf( buf, "%d\n%d\n", ctx->uidvalidity, ctx->nuid ); lseek( ctx->uvfd, 0, SEEK_SET ); if (write( ctx->uvfd, buf, n ) != n || ftruncate( ctx->uvfd, n ) || (UseFSync && fdatasync( ctx->uvfd ))) { error( "Maildir error: cannot write UIDVALIDITY.\n" ); @@ -686,7 +686,7 @@ maildir_store_uidval( maildir_store_t *ctx ) static int maildir_init_uidval( maildir_store_t *ctx ) { - ctx->gen.uidvalidity = time( 0 ); + ctx->uidvalidity = time( 0 ); ctx->nuid = 0; ctx->uvok = 0; #ifdef USE_DB @@ -754,14 +754,14 @@ maildir_uidval_lock( maildir_store_t *ctx ) } return maildir_init_uidval_new( ctx ); } - ctx->gen.uidvalidity = ((int *)value.data)[0]; + ctx->uidvalidity = ((int *)value.data)[0]; ctx->nuid = ((int *)value.data)[1]; } else #endif { lseek( ctx->uvfd, 0, SEEK_SET ); if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 || - (buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) { + (buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->uidvalidity, &ctx->nuid ) != 2)) { #if 1 /* In a generic driver, resetting the UID validity would be the right thing. * But this would mess up the sync state completely. So better bail out and @@ -1266,7 +1266,7 @@ maildir_get_box_path( store_t *gctx ) static void maildir_open_box( store_t *gctx, - void (*cb)( int sts, void *aux ), void *aux ) + void (*cb)( int sts, int uidvalidity, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; int ret; @@ -1279,7 +1279,7 @@ maildir_open_box( store_t *gctx, #ifndef USE_DB if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) { sys_error( "Maildir error: cannot write %s", uvpath ); - cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } #else @@ -1296,7 +1296,7 @@ maildir_open_box( store_t *gctx, goto fnok; } sys_error( "Maildir error: cannot write %s", uvpath ); - cb( DRV_BOX_BAD, aux ); + cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); return; } else { dbok: @@ -1308,7 +1308,7 @@ maildir_open_box( store_t *gctx, ret = maildir_uidval_lock( ctx ); bail: - cb( ret, aux ); + cb( ret, ctx->uidvalidity, aux ); } static int diff --git a/src/sync.c b/src/sync.c index d2d7ead..b829958 100644 --- a/src/sync.c +++ b/src/sync.c @@ -158,6 +158,7 @@ typedef struct { int maxuid[2]; /* highest UID that was already propagated */ int newmaxuid[2]; /* highest UID that is currently being propagated */ int uidval[2]; /* UID validity value */ + int newuidval[2]; /* UID validity obtained from driver */ int newuid[2]; /* TUID lookup makes sense only for UIDs >= this */ int mmaxxuid; /* highest expired UID on master */ } sync_vars_t; @@ -962,11 +963,11 @@ delete_state( sync_vars_t *svars ) } } -static void box_confirmed( int sts, void *aux ); +static void box_confirmed( int sts, int uidvalidity, void *aux ); static void box_confirmed2( sync_vars_t *svars, int t ); static void box_deleted( int sts, void *aux ); static void box_created( int sts, void *aux ); -static void box_opened( int sts, void *aux ); +static void box_opened( int sts, int uidvalidity, void *aux ); static void box_opened2( sync_vars_t *svars, int t ); static void load_box( sync_vars_t *svars, int t, int minwuid, int_array_t mexcs ); @@ -1002,7 +1003,6 @@ sync_boxes( store_t *ctx[], const char *names[], int present[], channel_conf_t * sync_bail3( svars ); return; } - ctx[t]->uidvalidity = UIDVAL_BAD; svars->drv[t] = ctx[t]->conf->driver; svars->drv[t]->set_bad_callback( ctx[t], store_bad, AUX ); } @@ -1043,7 +1043,7 @@ sync_boxes( store_t *ctx[], const char *names[], int present[], channel_conf_t * } static void -box_confirmed( int sts, void *aux ) +box_confirmed( int sts, int uidvalidity, void *aux ) { DECL_SVARS; @@ -1053,8 +1053,10 @@ box_confirmed( int sts, void *aux ) if (check_cancel( svars )) return; - if (sts == DRV_OK) + if (sts == DRV_OK) { svars->state[t] |= ST_PRESENT; + svars->newuidval[t] = uidvalidity; + } box_confirmed2( svars, t ); } @@ -1100,7 +1102,7 @@ box_confirmed2( sync_vars_t *svars, int t ) svars->drv[1-t]->delete_box( svars->ctx[1-t], box_deleted, INV_AUX ); } else { if (!(svars->chan->ops[t] & OP_CREATE)) { - box_opened( DRV_BOX_BAD, AUX ); + box_opened( DRV_BOX_BAD, UIDVAL_BAD, AUX ); } else { info( "Creating %s %s...\n", str_ms[t], svars->orig_name[t] ); svars->drv[t]->create_box( svars->ctx[t], box_created, AUX ); @@ -1142,7 +1144,7 @@ box_created( int sts, void *aux ) } static void -box_opened( int sts, void *aux ) +box_opened( int sts, int uidvalidity, void *aux ) { DECL_SVARS; @@ -1158,6 +1160,7 @@ box_opened( int sts, void *aux ) svars->ret = SYNC_FAIL; sync_bail( svars ); } else { + svars->newuidval[t] = uidvalidity; box_opened2( svars, t ); } } @@ -1180,13 +1183,13 @@ box_opened2( sync_vars_t *svars, int t ) fails = 0; for (t = 0; t < 2; t++) - if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != ctx[t]->uidvalidity) + if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t]) fails++; if (fails == 2) { error( "Error: channel %s: UIDVALIDITY of both master and slave changed\n" "(master got %d, expected %d; slave got %d, expected %d).\n", svars->chan->name, - ctx[M]->uidvalidity, svars->uidval[M], ctx[S]->uidvalidity, svars->uidval[S] ); + svars->newuidval[M], svars->uidval[M], svars->newuidval[S], svars->uidval[S] ); bail: svars->ret = SYNC_FAIL; sync_bail( svars ); @@ -1429,7 +1432,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux return; for (t = 0; t < 2; t++) { - if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->ctx[t]->uidvalidity) { + if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t]) { unsigned need = 0, got = 0; debug( "trying to re-approve uid validity of %s\n", str_ms[t] ); for (srec = svars->srecs; srec; srec = srec->next) { @@ -1460,7 +1463,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux error( "Error: channel %s, %s %s: Unable to recover from UIDVALIDITY change\n" "(got %d, expected %d).\n", svars->chan->name, str_ms[t], svars->orig_name[t], - svars->ctx[t]->uidvalidity, svars->uidval[t] ); + svars->newuidval[t], svars->uidval[t] ); goto uvchg; } notice( "Notice: channel %s, %s %s: Recovered from change of UIDVALIDITY.\n", @@ -1470,8 +1473,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux } if (svars->uidval[M] == UIDVAL_BAD || svars->uidval[S] == UIDVAL_BAD) { - svars->uidval[M] = svars->ctx[M]->uidvalidity; - svars->uidval[S] = svars->ctx[S]->uidvalidity; + svars->uidval[M] = svars->newuidval[M]; + svars->uidval[S] = svars->newuidval[S]; jFprintf( svars, "| %d %d\n", svars->uidval[M], svars->uidval[S] ); }