commit 74ea2bee0a1c78636544fd21ab21d2feed9f2656 Author: Oswald Buddenhagen <o...@kde.org> Date: Sun Apr 10 13:06:07 2011 +0200
employ alternative scheme to finding messages by TUID instead of SEARCHing every single message (which is slow and happens to be unreliabe with M$ Exchange 2010), just FETCH the new messages from the mailbox - the ones we just appended will be amongst them. src/drv_imap.c | 125 ++++++++++------------- src/drv_maildir.c | 31 +++--- src/isync.h | 19 ++-- src/run-tests.pl | 4 +- src/sync.c | 256 ++++++++++++++++++++------------------------- 5 files changed, 196 insertions(+), 239 deletions(-) diff --git a/src/drv_imap.c b/src/drv_imap.c index 496f93c..110df45 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -78,7 +78,6 @@ struct imap_cmd; typedef struct imap_store { store_t gen; const char *prefix; - int uidnext; /* from SELECT responses */ /* trash folder's existence is not confirmed yet */ enum { TrashUnknown, TrashChecking, TrashKnown } trashnc; unsigned got_namespace:1; @@ -599,7 +598,7 @@ static int parse_fetch( imap_store_t *ctx, list_t *list ) { list_t *tmp, *flags; - char *body = 0; + char *body = 0, *tuid = 0; imap_message_t *cur; msg_data_t *msgdata; struct imap_cmd *cmdp; @@ -660,6 +659,20 @@ parse_fetch( imap_store_t *ctx, list_t *list ) size = tmp->len; } else error( "IMAP error: unable to parse BODY[]\n" ); + } else if (!strcmp( "BODY[HEADER.FIELDS", tmp->val )) { + tmp = tmp->next; + if (is_list( tmp )) { + tmp = tmp->next; + if (!is_atom( tmp ) || strcmp( tmp->val, "]" )) + goto bfail; + tmp = tmp->next; + if (!is_atom( tmp ) || memcmp( tmp->val, "X-TUID: ", 8 )) + goto bfail; + tuid = tmp->val + 8; + } else { + bfail: + error( "IMAP error: unable to parse BODY[HEADER.FIELDS ...]\n" ); + } } } } @@ -687,6 +700,13 @@ parse_fetch( imap_store_t *ctx, list_t *list ) cur->gen.flags = mask; cur->gen.status = status; cur->gen.size = size; + cur->gen.srec = 0; + if (tuid) + strncpy( cur->gen.tuid, tuid, TUIDL ); + else + cur->gen.tuid[0] = 0; + if (ctx->gen.uidnext <= uid) /* in case the server sends no UIDNEXT */ + ctx->gen.uidnext = uid + 1; } free_list( list ); @@ -728,7 +748,7 @@ parse_response_code( imap_store_t *ctx, struct imap_cmd *cmd, char *s ) return RESP_CANCEL; } } else if (!strcmp( "UIDNEXT", arg )) { - if (!(arg = next_arg( &s )) || (ctx->uidnext = strtol( arg, &p, 10 ), *p)) { + if (!(arg = next_arg( &s )) || (ctx->gen.uidnext = strtol( arg, &p, 10 ), *p)) { error( "IMAP error: malformed NEXTUID status\n" ); return RESP_CANCEL; } @@ -754,35 +774,6 @@ parse_response_code( imap_store_t *ctx, struct imap_cmd *cmd, char *s ) } static void -parse_search( imap_store_t *ctx, char *cmd ) -{ - char *arg; - struct imap_cmd *cmdp; - int uid; - - if (!(arg = next_arg( &cmd ))) - uid = -1; - else if (!(uid = atoi( arg ))) { - error( "IMAP error: malformed SEARCH response\n" ); - return; - } else if (next_arg( &cmd )) { - warn( "IMAP warning: SEARCH returns multiple matches\n" ); - uid = -1; /* to avoid havoc */ - } - - /* Find the first command that expects a UID - this is guaranteed - * to come in-order, as there are no other means to identify which - * SEARCH response belongs to which request. - */ - for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next) - if (cmdp->param.uid == -1) { - ((struct imap_cmd_out_uid *)cmdp)->out_uid = uid; - return; - } - error( "IMAP error: unexpected SEARCH response (UID %u)\n", uid ); -} - -static void parse_list_rsp( imap_store_t *ctx, char *cmd ) { char *arg; @@ -858,8 +849,6 @@ imap_socket_read( void *aux ) parse_capability( ctx, cmd ); else if (!strcmp( "LIST", arg )) parse_list_rsp( ctx, cmd ); - else if (!strcmp( "SEARCH", arg )) - parse_search( ctx, cmd ); else if ((arg1 = next_arg( &cmd ))) { if (!strcmp( "EXISTS", arg1 )) ctx->gen.count = atoi( arg ); @@ -974,7 +963,7 @@ get_cmd_result_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) if (response != RESP_OK) { return done_imap_cmd( ctx, ocmd, response ); } else { - ctx->uidnext = 0; + ctx->gen.uidnext = 0; if (ocmd->param.to_trash) ctx->trashnc = TrashKnown; ocmd->param.create = 0; @@ -1412,15 +1401,15 @@ imap_prepare_opts( store_t *gctx, int opts ) struct imap_cmd_select { struct imap_cmd_simple gen; - int minuid, maxuid, *excs, nexcs; + int minuid, maxuid, newuid, *excs, nexcs; }; static int imap_select_p2( imap_store_t *, struct imap_cmd *, int ); -static int imap_submit_select2( imap_store_t *, const char *, struct imap_cmd_refcounted_state * ); +static int imap_submit_select2( imap_store_t *, const char *, int, struct imap_cmd_refcounted_state * ); static int imap_select2_p2( imap_store_t *, struct imap_cmd *, int ); static int -imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, +imap_select( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, int (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; @@ -1433,13 +1422,14 @@ imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, prefix = ctx->prefix; } - ctx->uidnext = -1; + ctx->gen.uidnext = -1; 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->newuid = newuid; cmd->excs = excs; cmd->nexcs = nexcs; return imap_exec( ctx, &cmd->gen.gen, imap_select_p2, @@ -1473,16 +1463,25 @@ imap_select_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) if (i != j) bl += sprintf( buf + bl, ":%d", cmdp->excs[i] ); } - if (imap_submit_select2( ctx, buf, sts ) < 0) { + if (imap_submit_select2( ctx, buf, 0, sts ) < 0) { free( cmdp->excs ); return -1; } } if (cmdp->maxuid == INT_MAX) - cmdp->maxuid = ctx->uidnext >= 0 ? ctx->uidnext - 1 : 1000000000; + cmdp->maxuid = ctx->gen.uidnext >= 0 ? ctx->gen.uidnext - 1 : 1000000000; if (cmdp->maxuid >= cmdp->minuid) { - sprintf( buf, "%d:%d", cmdp->minuid, cmdp->maxuid ); - if (imap_submit_select2( ctx, buf, sts ) < 0) { + if ((ctx->gen.opts & OPEN_FIND) && cmdp->minuid < cmdp->newuid) { + sprintf( buf, "%d:%d", cmdp->minuid, cmdp->newuid - 1 ); + if (imap_submit_select2( ctx, buf, 0, sts ) < 0) { + free( cmdp->excs ); + return -1; + } + sprintf( buf, "%d:%d", cmdp->newuid, cmdp->maxuid ); + } else { + sprintf( buf, "%d:%d", cmdp->minuid, cmdp->maxuid ); + } + if (imap_submit_select2( ctx, buf, (ctx->gen.opts & OPEN_FIND), sts ) < 0) { free( cmdp->excs ); return -1; } @@ -1493,12 +1492,13 @@ imap_select_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) } static int -imap_submit_select2( imap_store_t *ctx, const char *buf, struct imap_cmd_refcounted_state *sts ) +imap_submit_select2( imap_store_t *ctx, const char *buf, int tuids, struct imap_cmd_refcounted_state *sts ) { return imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_select2_p2, - "UID FETCH %s (UID%s%s)", buf, + "UID FETCH %s (UID%s%s%s)", buf, (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "", - (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "" ); + (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "", + tuids ? " BODY.PEEK[HEADER.FIELDS (X-TUID)]" : ""); } static int @@ -1684,35 +1684,18 @@ imap_store_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int resp return cmdp->callback( response, cmdp->out_uid, cmdp->callback_aux ); } -/******************* imap_find_msg *******************/ - -static int imap_find_msg_p2( imap_store_t *, struct imap_cmd *, int ); +/******************* imap_find_new_msgs *******************/ static int -imap_find_msg( store_t *gctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ) +imap_find_new_msgs( store_t *gctx, + int (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; - 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 */ - return imap_exec( ctx, &cmd->gen, imap_find_msg_p2, - "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid ); -} - -static int -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; + struct imap_cmd_simple *cmd; - transform_msg_response( &response ); - if (response != DRV_OK) - return cmdp->callback( response, -1, cmdp->callback_aux ); - else - return cmdp->callback( cmdp->out_uid <= 0 ? DRV_MSG_BAD : DRV_OK, - cmdp->out_uid, cmdp->callback_aux ); + INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux) + return imap_exec( (imap_store_t *)ctx, &cmd->gen, imap_done_simple_box, + "UID FETCH %d:1000000000 (UID BODY.PEEK[HEADER.FIELDS (X-TUID)])", ctx->gen.uidnext ); } /******************* imap_list *******************/ @@ -1927,7 +1910,7 @@ struct driver imap_driver = { imap_select, imap_fetch_msg, imap_store_msg, - imap_find_msg, + imap_find_new_msgs, imap_set_flags, imap_trash_msg, imap_close, diff --git a/src/drv_maildir.c b/src/drv_maildir.c index ed10118..50ef795 100644 --- a/src/drv_maildir.c +++ b/src/drv_maildir.c @@ -24,6 +24,7 @@ #include "isync.h" +#include <assert.h> #include <limits.h> #include <stdlib.h> #include <string.h> @@ -57,13 +58,12 @@ typedef struct maildir_store_conf { typedef struct maildir_message { message_t gen; char *base; - char tuid[TUIDL]; } maildir_message_t; typedef struct maildir_store { store_t gen; int uvfd, uvok, nuid; - int minuid, maxuid, nexcs, *excs; + int minuid, maxuid, newuid, nexcs, *excs; #ifdef USE_DB DB *db; #endif /* USE_DB */ @@ -635,7 +635,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist ) goto again; } uid = entry->uid; - if (ctx->gen.opts & (OPEN_SIZE|OPEN_FIND)) + if ((ctx->gen.opts & OPEN_SIZE) || ((ctx->gen.opts & OPEN_FIND) && uid >= ctx->newuid)) nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s", subdirs[entry->recent], entry->base ); #ifdef USE_DB } else if (ctx->db) { @@ -644,7 +644,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist ) return ret; } entry->uid = uid; - if (ctx->gen.opts & (OPEN_SIZE|OPEN_FIND)) + if ((ctx->gen.opts & OPEN_SIZE) || ((ctx->gen.opts & OPEN_FIND) && uid >= ctx->newuid)) nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s", subdirs[entry->recent], entry->base ); #endif /* USE_DB */ } else { @@ -683,7 +683,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist ) goto notok; entry->size = st.st_size; } - if (ctx->gen.opts & OPEN_FIND) { + if ((ctx->gen.opts & OPEN_FIND) && uid >= ctx->newuid) { if (!(f = fopen( buf, "r" ))) goto notok; while (fgets( nbuf, sizeof(nbuf), f )) { @@ -712,7 +712,8 @@ maildir_init_msg( maildir_store_t *ctx, maildir_message_t *msg, msg_t *entry ) msg->base = entry->base; entry->base = 0; /* prevent deletion */ msg->gen.size = entry->size; - strncpy( msg->tuid, entry->tuid, TUIDL ); + msg->gen.srec = 0; + strncpy( msg->gen.tuid, entry->tuid, TUIDL ); if (entry->recent) msg->gen.status |= M_RECENT; if (ctx->gen.opts & OPEN_FLAGS) { @@ -762,7 +763,7 @@ maildir_prepare_opts( store_t *gctx, int opts ) } static int -maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, +maildir_select( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, int (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; @@ -776,6 +777,7 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, ctx->minuid = minuid; ctx->maxuid = maxuid; + ctx->newuid = newuid; ctx->excs = nfrealloc( excs, nexcs * sizeof(int) ); ctx->nexcs = nexcs; @@ -1037,16 +1039,11 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, } static int -maildir_find_msg( store_t *gctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ) +maildir_find_new_msgs( store_t *gctx ATTR_UNUSED, + int (*cb)( int sts, void *aux ) ATTR_UNUSED, void *aux ATTR_UNUSED ) { - 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 ); + assert( !"maildir_find_new_msgs is not supposed to be called" ); + return -1; } static int @@ -1258,7 +1255,7 @@ struct driver maildir_driver = { maildir_select, maildir_fetch_msg, maildir_store_msg, - maildir_find_msg, + maildir_find_new_msgs, maildir_set_flags, maildir_trash_msg, maildir_close, diff --git a/src/isync.h b/src/isync.h index 3ff119f..7c49222 100644 --- a/src/isync.h +++ b/src/isync.h @@ -186,6 +186,8 @@ typedef struct group_conf { #define M_DEAD (1<<1) /* expunged */ #define M_FLAGS (1<<2) /* flags fetched */ +#define TUIDL 12 + typedef struct message { struct message *next; struct sync_rec *srec; @@ -193,6 +195,7 @@ typedef struct message { size_t size; /* zero implies "not fetched" */ int uid; unsigned char flags, status; + char tuid[TUIDL]; } message_t; /* For opts, both in store and driver_t->select() */ @@ -220,6 +223,7 @@ typedef struct store { char *path; /* own */ message_t *msgs; /* own */ int uidvalidity; + int uidnext; /* from SELECT responses */ unsigned opts; /* maybe preset? */ /* note that the following do _not_ reflect stats from msgs, but mailbox totals */ int count; /* # of messages */ @@ -262,8 +266,6 @@ typedef struct { */ #define DRV_CRLF 1 -#define TUIDL 12 - struct driver { int flags; @@ -303,8 +305,9 @@ struct driver { /* Open the previously prepared mailbox and load the message attributes needed to * perform the requested operations. Consider only messages with UIDs between minuid * and maxuid (inclusive) and those named in the excs array (smaller than minuid). - * The driver takes ownership of the excs array. */ - int (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs, + * The driver takes ownership of the excs array. Messages below newuid do not need + * to have the TUID populated even if OPEN_FIND is set. */ + int (*select)( store_t *ctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, int (*cb)( int sts, void *aux ), void *aux ); /* Fetch the contents and flags of the given message from the current mailbox. */ @@ -316,9 +319,11 @@ struct driver { int (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash, int (*cb)( int sts, int uid, void *aux ), void *aux ); - /* Find a message by its temporary UID header to determine its real UID. */ - int (*find_msg)( store_t *ctx, const char *tuid, - int (*cb)( int sts, int uid, void *aux ), void *aux ); + /* Index the messages which have newly appeared in the mailbox, including their + * temporary UID headers. This is needed if store_msg() does not guarantee returning + * a UID; otherwise the driver needs to implement only the OPEN_FIND flag. */ + int (*find_new_msgs)( store_t *ctx, + int (*cb)( int sts, void *aux ), void *aux ); /* Add/remove the named flags to/from the given message. The message may be either * a pre-fetched one (in which case the in-memory representation is updated), diff --git a/src/run-tests.pl b/src/run-tests.pl index 396c474..f2b4736 100755 --- a/src/run-tests.pl +++ b/src/run-tests.pl @@ -366,7 +366,7 @@ sub showstate($) close FILE; return; } - if (!/^1:(\d+) 1:(\d+):(\d+)\n$/) { + if (!/^1:(\d+):0 1:(\d+):(\d+):0\n$/) { print STDERR " Malformed sync state header '$_'.\n"; close FILE; return; @@ -507,7 +507,7 @@ sub ckstate($@) return 1; } chomp($l); - my $xl = "1:".shift(@T)." 1:".shift(@T).":".shift(@T); + my $xl = "1:".shift(@T).":0 1:".shift(@T).":".shift(@T).":0"; if ($l ne $xl) { print STDERR "Sync state header mismatch: '$l' instead of '$xl'.\n"; return 1; diff --git a/src/sync.c b/src/sync.c index 85f529a..85027b8 100644 --- a/src/sync.c +++ b/src/sync.c @@ -96,6 +96,7 @@ make_flags( int flags, char *buf ) #define S_EXPIRE (1<<5) #define S_NEXPIRE (1<<6) #define S_EXP_S (1<<7) +#define S_FIND (1<<8) #define mvBit(in,ib,ob) ((unsigned char)(((unsigned)in) * (ob) / (ib))) @@ -146,13 +147,10 @@ typedef struct { store_t *ctx[2]; driver_t *drv[2]; int state[2], 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]; int flags_total[2], flags_done[2]; int trash_total[2], trash_done[2]; - int maxuid[2], uidval[2], smaxxuid, lfd; - unsigned find:1; + int maxuid[2], uidval[2], uidnext[2], smaxxuid, lfd; } sync_vars_t; #define AUX &svars->t[t] @@ -162,10 +160,8 @@ typedef struct { /* operation dependencies: select(S): - - find_old(S): select(S) - select(M): find_old(S) | - - find_old(M): select(M) - new(M), new(S), flags(M): find_old(M) & find_old(S) + select(M): select(S) | - + new(M), new(S), flags(M): select(M) & select(S) flags(S): count(new(S)) find_new(x): new(x) trash(x): flags(x) @@ -173,9 +169,9 @@ typedef struct { cleanup: close(M) & close(S) */ -#define ST_SENT_FIND_OLD (1<<0) +#define ST_SELECTED (1<<0) #define ST_SENT_NEW (1<<1) -#define ST_SENT_FIND_NEW (1<<2) +#define ST_FOUND_NEW (1<<2) #define ST_SENT_FLAGS (1<<3) #define ST_SENT_TRASH (1<<4) #define ST_CLOSED (1<<5) @@ -185,6 +181,51 @@ typedef struct { #define ST_DID_EXPUNGE (1<<16) +static void +match_tuids( sync_vars_t *svars, int t ) +{ + sync_rec_t *srec; + message_t *tmsg, *ntmsg = 0; + const char *diag; + + for (srec = svars->srecs; srec; srec = srec->next) { + if (srec->status & S_DEAD) + continue; + if (srec->uid[t] == -2 && srec->tuid[0]) { + debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid ); + for (tmsg = ntmsg; tmsg; tmsg = tmsg->next) { + if (tmsg->status & M_DEAD) + continue; + if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) { + diag = (tmsg == ntmsg) ? "adjacently" : "after gap"; + goto mfound; + } + } + for (tmsg = svars->ctx[t]->msgs; tmsg != ntmsg; tmsg = tmsg->next) { + if (tmsg->status & M_DEAD) + continue; + if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) { + diag = "after reset"; + goto mfound; + } + } + debug( " -> TUID lost\n" ); + Fprintf( svars->jfp, "& %d %d\n", srec->uid[M], srec->uid[S] ); + srec->flags = 0; + srec->tuid[0] = 0; + continue; + mfound: + debug( " -> new UID %d %s\n", tmsg->uid, diag ); + Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], tmsg->uid ); + tmsg->srec = srec; + ntmsg = tmsg->next; + srec->uid[t] = tmsg->uid; + srec->tuid[0] = 0; + } + } +} + + typedef struct copy_vars { int (*cb)( int sts, int uid, struct copy_vars *vars ); void *aux; @@ -363,9 +404,7 @@ stats( sync_vars_t *svars ) cols = 36; if (!(DFlags & QUIET)) { for (t = 0; t < 2; t++) { - l = sprintf( buf[t], "?%d/%d +%d/%d *%d/%d #%d/%d", - svars->find_old_done[t] + svars->find_new_done[t], - svars->find_old_total[t] + svars->find_new_total[t], + l = sprintf( buf[t], "+%d/%d *%d/%d #%d/%d", svars->new_done[t], svars->new_total[t], svars->flags_done[t], svars->flags_total[t], svars->trash_done[t], svars->trash_total[t] ); @@ -475,7 +514,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, struct stat st; struct flock lck; char fbuf[16]; /* enlarge when support for keywords is added */ - char buf[64]; + char buf[128], buf1[64], buf2[64]; svars = nfcalloc( sizeof(*svars) ); svars->t[1] = 1; @@ -567,7 +606,9 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, sync_bail( svars ); return; } - if (sscanf( buf, "%d:%d %d:%d:%d", &svars->uidval[M], &svars->maxuid[M], &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S]) != 5) { + if (sscanf( buf, "%63s %63s", buf1, buf2 ) != 2 || + sscanf( buf1, "%d:%d:%d", &svars->uidval[M], &svars->maxuid[M], &svars->uidnext[M] ) < 2 || + sscanf( buf2, "%d:%d:%d:%d", &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S], &svars->uidnext[S] ) < 3) { error( "Error: invalid sync state header in %s\n", svars->dname ); goto jbail; } @@ -630,7 +671,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, } if (buf[0] == '#' ? (t3 = 0, (sscanf( buf + 2, "%d %d %n", &t1, &t2, &t3 ) < 2) || !t3 || (t - t3 != TUIDL + 3)) : - buf[0] == '(' || buf[0] == ')' ? + buf[0] == '(' || buf[0] == ')' || buf[0] == '{' || buf[0] == '}' ? (sscanf( buf + 2, "%d", &t1 ) != 1) : buf[0] == '+' || buf[0] == '&' || buf[0] == '-' || buf[0] == '|' || buf[0] == '/' || buf[0] == '\\' ? (sscanf( buf + 2, "%d %d", &t1, &t2 ) != 2) : @@ -643,6 +684,10 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, svars->maxuid[M] = t1; else if (buf[0] == ')') svars->maxuid[S] = t1; + else if (buf[0] == '{') + svars->uidnext[M] = t1; + else if (buf[0] == '}') + svars->uidnext[S] = t1; else if (buf[0] == '|') { svars->uidval[M] = t1; svars->uidval[S] = t2; @@ -790,15 +835,14 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan, opts[S] |= OPEN_OLD|OPEN_FLAGS; if (srec->tuid[0]) { if (srec->uid[M] == -2) - opts[M] |= OPEN_OLD|OPEN_FIND; + opts[M] |= OPEN_NEW|OPEN_FIND, svars->state[M] |= S_FIND; else if (srec->uid[S] == -2) - opts[S] |= OPEN_OLD|OPEN_FIND; + opts[S] |= OPEN_NEW|OPEN_FIND, svars->state[S] |= S_FIND; } } svars->drv[M]->prepare_opts( ctx[M], opts[M] ); svars->drv[S]->prepare_opts( ctx[S], opts[S] ); - svars->find = line != 0; if (!svars->smaxxuid && select_box( svars, M, (ctx[M]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 )) return; select_box( svars, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 ); @@ -825,89 +869,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 ); -} - -typedef struct { - void *aux; - 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 int -box_selected( int sts, void *aux ) -{ - SVARS(aux) - find_vars_t *fv; - sync_rec_t *srec; - - if (check_ret( sts, svars )) - return -1; - if (svars->uidval[t] >= 0 && svars->uidval[t] != svars->ctx[t]->uidvalidity) { - error( "Error: UIDVALIDITY of %s changed (got %d, expected %d)\n", - str_ms[t], svars->ctx[t]->uidvalidity, svars->uidval[t] ); - svars->ret |= SYNC_FAIL; - cancel_sync( svars ); - return -1; - } - info( "%s: %d messages, %d recent\n", str_ms[t], svars->ctx[t]->count, svars->ctx[t]->recent ); - - if (svars->find) { - /* - * Alternatively, the TUIDs could be fetched into the messages and - * looked up here. This would make the search faster (probably) and - * save roundtrips. On the downside, quite some additional data would - * have to be fetched for every message and the IMAP driver would be - * more complicated. This is a corner case anyway, so why bother. - */ - debug( "finding previously copied messages\n" ); - for (srec = svars->srecs; srec; srec = srec->next) { - if (srec->status & S_DEAD) - continue; - if (srec->uid[t] == -2 && srec->tuid[0]) { - debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid ); - svars->find_old_total[t]++; - stats( svars ); - 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; - } - } - } - svars->state[t] |= ST_SENT_FIND_OLD; - return msgs_found_sel( svars, t ); -} - -static int -msg_found_sel( int sts, int uid, void *aux ) -{ - find_vars_t *vars = (find_vars_t *)aux; - SVARS(vars->aux) - - if (check_ret_aux( sts, svars, vars )) - return -1; - switch (sts) { - case DRV_OK: - debug( " -> new UID %d\n", uid ); - Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], vars->srec->uid[M], vars->srec->uid[S], uid ); - vars->srec->uid[t] = uid; - vars->srec->tuid[0] = 0; - break; - default: - debug( " -> TUID lost\n" ); - Fprintf( svars->jfp, "& %d %d\n", vars->srec->uid[M], vars->srec->uid[S] ); - vars->srec->flags = 0; - vars->srec->tuid[0] = 0; - break; - } - free( vars ); - svars->find_old_done[t]++; - stats( svars ); - return msgs_found_sel( svars, t ); + return svars->drv[t]->select( svars->ctx[t], minwuid, maxwuid, svars->uidnext[t], mexcs, nmexcs, box_selected, AUX ); } typedef struct { @@ -925,8 +887,9 @@ static void msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_ static int msgs_copied( sync_vars_t *svars, int t ); static int -msgs_found_sel( sync_vars_t *svars, int t ) +box_selected( int sts, void *aux ) { + SVARS(aux) sync_rec_t *srec, *nsrec = 0; message_t *tmsg; copy_vars_t *cv; @@ -936,8 +899,24 @@ msgs_found_sel( sync_vars_t *svars, int t ) int sflags, nflags, aflags, dflags, nex; 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; + if (check_ret( sts, svars )) + return -1; + svars->state[t] |= ST_SELECTED; + if (svars->uidval[t] >= 0 && svars->uidval[t] != svars->ctx[t]->uidvalidity) { + error( "Error: UIDVALIDITY of %s changed (got %d, expected %d)\n", + str_ms[t], svars->ctx[t]->uidvalidity, svars->uidval[t] ); + svars->ret |= SYNC_FAIL; + cancel_sync( svars ); + return -1; + } + info( "%s: %d messages, %d recent\n", str_ms[t], svars->ctx[t]->count, svars->ctx[t]->recent ); + + if (svars->state[t] & S_FIND) { + svars->state[t] &= ~S_FIND; + debug( "matching previously copied messages on %s\n", str_ms[t] ); + match_tuids( svars, t ); + } + Fprintf( svars->jfp, "%c %d\n", "{}"[t], svars->ctx[t]->uidnext ); /* * Mapping tmsg -> srec (this variant) is dog slow for new messages. @@ -947,6 +926,8 @@ msgs_found_sel( sync_vars_t *svars, int t ) */ debug( "matching messages on %s against sync records\n", str_ms[t] ); for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next) { + if (tmsg->srec) /* found by TUID */ + continue; uid = tmsg->uid; if (DFlags & DEBUG) { make_flags( tmsg->flags, fbuf ); @@ -1036,7 +1017,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) return select_box( svars, M, minwuid, mexcs, nmexcs ); } - if (!(svars->state[1-t] & ST_SENT_FIND_OLD) || svars->find_old_done[1-t] < svars->find_old_total[1-t]) + if (!(svars->state[1-t] & ST_SELECTED)) return 0; if (svars->uidval[M] < 0 || svars->uidval[S] < 0) { @@ -1277,6 +1258,8 @@ msg_copied( int sts, int uid, copy_vars_t *vars ) switch (sts) { case SYNC_OK: + if (uid < 0) + svars->state[t] |= S_FIND; msg_copied_p2( svars, vars->srec, t, vars->msg, uid ); break; case SYNC_NOGOOD: @@ -1315,60 +1298,48 @@ 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 msgs_found_new( int sts, void *aux ); +static int msgs_new_done( sync_vars_t *svars, int t ); static int sync_close( sync_vars_t *svars, int t ); static int 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; - debug( "finding just copied messages on %s\n", str_ms[t] ); - for (srec = svars->srecs; srec; srec = srec->next) { - if (srec->status & S_DEAD) - continue; - if (srec->tuid[0] && srec->uid[t] == -2) { - debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid ); - svars->find_new_total[t]++; - stats( svars ); - 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; - } + if (svars->state[t] & S_FIND) { + debug( "finding just copied messages on %s\n", str_ms[t] ); + return svars->drv[t]->find_new_msgs( svars->ctx[t], msgs_found_new, AUX ); + } else { + return msgs_new_done( svars, t ); } - svars->state[t] |= ST_SENT_FIND_NEW; - return sync_close( svars, t ); } static int -msg_found_new( int sts, int uid, void *aux ) +msgs_found_new( int sts, void *aux ) { - find_vars_t *vars = (find_vars_t *)aux; - SVARS(vars->aux) + SVARS(aux) - if (check_ret_aux( sts, svars, vars )) + if (check_ret( sts, svars )) return -1; switch (sts) { case DRV_OK: - debug( " -> new UID %d\n", uid ); + debug( "matching just copied messages on %s\n", str_ms[t] ); break; default: - warn( "Warning: cannot find newly stored message %." stringify(TUIDL) "s on %s.\n", vars->srec->tuid, str_ms[t] ); - uid = 0; + warn( "Warning: cannot find newly stored messages on %s.\n", str_ms[t] ); break; } - Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], vars->srec->uid[M], vars->srec->uid[S], uid ); - vars->srec->uid[t] = uid; - vars->srec->tuid[0] = 0; - free( vars ); - svars->find_new_done[t]++; - stats( svars ); + match_tuids( svars, t ); + return msgs_new_done( svars, t ); +} + +static int +msgs_new_done( sync_vars_t *svars, int t ) +{ + Fprintf( svars->jfp, "%c %d\n", "{}"[t], svars->ctx[t]->uidnext ); + svars->state[t] |= ST_FOUND_NEW; return sync_close( svars, t ); } @@ -1536,8 +1507,7 @@ static int box_closed_p2( sync_vars_t *svars, int t ); static int 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] || + if ((~svars->state[t] & (ST_FOUND_NEW|ST_SENT_TRASH)) || svars->trash_done[t] < svars->trash_total[t]) return 0; @@ -1610,7 +1580,9 @@ box_closed_p2( sync_vars_t *svars, int t ) } } - Fprintf( svars->nfp, "%d:%d %d:%d:%d\n", svars->uidval[M], svars->maxuid[M], svars->uidval[S], svars->smaxxuid, svars->maxuid[S] ); + Fprintf( svars->nfp, "%d:%d:%d %d:%d:%d:%d\n", + svars->uidval[M], svars->maxuid[M], svars->ctx[M]->uidnext, + svars->uidval[S], svars->smaxxuid, svars->maxuid[S], svars->ctx[S]->uidnext ); for (srec = svars->srecs; srec; srec = srec->next) { if (srec->status & S_DEAD) continue; ------------------------------------------------------------------------------ Xperia(TM) PLAY It's a major breakthrough. An authentic gaming smartphone on the nation's most reliable network. And it wants your games. http://p.sf.net/sfu/verizon-sfdev _______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel