commit e9152f606d1301442ee1bcc3f9197f7a9bfe339a 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 | 120 ++++++++++-------------- src/drv_maildir.c | 32 +++---- src/isync.h | 19 +++-- src/run-tests.pl | 4 +- src/sync.c | 229 +++++++++++++++++++++------------------------ 5 files changed, 183 insertions(+), 221 deletions(-) diff --git a/src/drv_imap.c b/src/drv_imap.c index a22c4c6..07fab53 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -79,7 +79,6 @@ typedef struct imap_store { store_t gen; const char *prefix; int ref_count; - int uidnext; /* from SELECT responses */ /* trash folder's existence is not confirmed yet */ enum { TrashUnknown, TrashChecking, TrashKnown } trashnc; unsigned got_namespace:1; @@ -602,7 +601,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; @@ -663,6 +662,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" ); + } } } } @@ -690,6 +703,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 ); @@ -731,7 +751,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; } @@ -757,35 +777,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; @@ -861,8 +852,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 ); @@ -980,7 +969,7 @@ get_cmd_result_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response ) if (response != RESP_OK) { 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; @@ -1447,7 +1436,7 @@ imap_select( store_t *gctx, int create, prefix = ctx->prefix; } - ctx->uidnext = -1; + ctx->gen.uidnext = -1; INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux) cmd->gen.param.create = create; @@ -1458,11 +1447,11 @@ imap_select( store_t *gctx, int create, /******************* imap_load *******************/ -static int imap_submit_load( imap_store_t *, const char *, struct imap_cmd_refcounted_state * ); +static int imap_submit_load( imap_store_t *, const char *, int, struct imap_cmd_refcounted_state * ); static void imap_load_p2( imap_store_t *, struct imap_cmd *, int ); static void -imap_load( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, +imap_load( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, void (*cb)( int sts, void *aux ), void *aux ) { imap_store_t *ctx = (imap_store_t *)gctx; @@ -1487,14 +1476,21 @@ imap_load( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, if (i != j) bl += sprintf( buf + bl, ":%d", excs[i] ); } - if (imap_submit_load( ctx, buf, sts ) < 0) + if (imap_submit_load( ctx, buf, 0, sts ) < 0) goto done; } if (maxuid == INT_MAX) - maxuid = ctx->uidnext >= 0 ? ctx->uidnext - 1 : 1000000000; + maxuid = ctx->gen.uidnext >= 0 ? ctx->gen.uidnext - 1 : 1000000000; if (maxuid >= minuid) { - sprintf( buf, "%d:%d", minuid, maxuid ); - imap_submit_load( ctx, buf, sts ); + if ((ctx->gen.opts & OPEN_FIND) && minuid < newuid) { + sprintf( buf, "%d:%d", minuid, newuid - 1 ); + if (imap_submit_load( ctx, buf, 0, sts ) < 0) + goto done; + sprintf( buf, "%d:%d", newuid, maxuid ); + } else { + sprintf( buf, "%d:%d", minuid, maxuid ); + } + imap_submit_load( ctx, buf, (ctx->gen.opts & OPEN_FIND), sts ); } done: free( excs ); @@ -1503,12 +1499,13 @@ imap_load( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, } static int -imap_submit_load( imap_store_t *ctx, const char *buf, struct imap_cmd_refcounted_state *sts ) +imap_submit_load( 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_load_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 void @@ -1693,35 +1690,18 @@ imap_store_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int resp cmdp->callback( response, cmdp->out_uid, cmdp->callback_aux ); } -/******************* imap_find_msg *******************/ - -static void imap_find_msg_p2( imap_store_t *, struct imap_cmd *, int ); +/******************* imap_find_new_msgs *******************/ static void -imap_find_msg( store_t *gctx, const char *tuid, - void (*cb)( int sts, int uid, void *aux ), void *aux ) +imap_find_new_msgs( store_t *gctx, + void (*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 */ - imap_exec( ctx, &cmd->gen, imap_find_msg_p2, - "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid ); -} - -static void -imap_find_msg_p2( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, int response ) -{ - struct imap_cmd_out_uid *cmdp = (struct imap_cmd_out_uid *)cmd; + struct imap_cmd_simple *cmd; - 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 ); + INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux) + 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 *******************/ @@ -1917,7 +1897,7 @@ struct driver imap_driver = { imap_load, 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 2173e60..3bfccdd 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 */ @@ -642,7 +642,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) { @@ -651,7 +651,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 { @@ -696,7 +696,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist ) } 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" ))) { if (errno != ENOENT) { sys_error( "Maildir error: cannot open %s", buf ); @@ -730,7 +730,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) { @@ -861,7 +862,7 @@ maildir_prepare_opts( store_t *gctx, int opts ) } static void -maildir_load( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs, +maildir_load( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, void (*cb)( int sts, void *aux ), void *aux ) { maildir_store_t *ctx = (maildir_store_t *)gctx; @@ -871,6 +872,7 @@ maildir_load( 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; @@ -1086,18 +1088,10 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, } static void -maildir_find_msg( store_t *gctx, const char *tuid, - void (*cb)( int sts, int uid, void *aux ), void *aux ) +maildir_find_new_msgs( store_t *gctx ATTR_UNUSED, + void (*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 )) { - cb( DRV_OK, msg->uid, aux ); - return; - } - cb( DRV_MSG_BAD, -1, aux ); + assert( !"maildir_find_new_msgs is not supposed to be called" ); } static void @@ -1324,7 +1318,7 @@ struct driver maildir_driver = { maildir_load, 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 0546ac8..fa7d082 100644 --- a/src/isync.h +++ b/src/isync.h @@ -184,6 +184,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; @@ -191,6 +193,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() */ @@ -217,6 +220,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 */ @@ -255,8 +259,6 @@ typedef struct { */ #define DRV_CRLF 1 -#define TUIDL 12 - struct driver { int flags; @@ -298,8 +300,9 @@ struct driver { /* 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. */ - void (*load)( 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. */ + void (*load)( store_t *ctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, void (*cb)( int sts, void *aux ), void *aux ); /* Fetch the contents and flags of the given message from the current mailbox. */ @@ -311,9 +314,11 @@ struct driver { void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash, void (*cb)( int sts, int uid, void *aux ), void *aux ); - /* Find a message by its temporary UID header to determine its real UID. */ - void (*find_msg)( store_t *ctx, const char *tuid, - void (*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. */ + void (*find_new_msgs)( store_t *ctx, + void (*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 250af21..8f4d210 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,15 +147,13 @@ typedef struct { store_t *ctx[2]; driver_t *drv[2]; int state[2], ref_count, ret, lfd; - 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]; /* highest UID that was already propagated */ int uidval[2]; /* UID validity value */ + int uidnext[2]; /* next expected UID; TUID lookup makes sense only for lower UIDs */ int smaxxuid; /* highest expired UID on slave */ - unsigned find:1; } sync_vars_t; static void sync_ref( sync_vars_t *svars ) { ++svars->ref_count; } @@ -190,10 +189,8 @@ static int check_cancel( sync_vars_t *svars ); /* 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) @@ -201,9 +198,9 @@ static int check_cancel( sync_vars_t *svars ); cleanup: close(M) & close(S) */ -#define ST_SENT_FIND_OLD (1<<0) +#define ST_LOADED (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) @@ -214,6 +211,51 @@ static int check_cancel( sync_vars_t *svars ); #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 { void (*cb)( int sts, int uid, struct copy_vars *vars ); void *aux; @@ -410,9 +452,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] ); @@ -591,7 +631,7 @@ box_selected( int sts, void *aux ) 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]; if (check_ret( sts, aux )) return; @@ -668,7 +708,9 @@ box_selected( int sts, void *aux ) 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; } @@ -731,7 +773,7 @@ box_selected( int sts, void *aux ) } 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) : @@ -744,6 +786,10 @@ box_selected( int sts, void *aux ) 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; @@ -900,15 +946,14 @@ box_selected( int sts, void *aux ) 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 && load_box( svars, M, (ctx[M]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 )) return; load_box( svars, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 ); @@ -935,75 +980,7 @@ load_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs ) maxwuid = 0; info( "Loading %s...\n", str_ms[t] ); debug( maxwuid == INT_MAX ? "loading %s [%d,inf]\n" : "loading %s [%d,%d]\n", str_ms[t], minwuid, maxwuid ); - DRIVER_CALL_RET(load( svars->ctx[t], minwuid, maxwuid, mexcs, nmexcs, box_loaded, AUX )); -} - -typedef struct { - void *aux; - sync_rec_t *srec; -} find_vars_t; - -static void msg_found_sel( int sts, int uid, void *aux ); -static void msgs_found_sel( sync_vars_t *svars, int t ); - -static void -box_loaded( int sts, void *aux ) -{ - find_vars_t *fv; - sync_rec_t *srec; - - SVARS_CHECK_RET; - 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; - DRIVER_CALL(find_msg( svars->ctx[t], srec->tuid, msg_found_sel, fv )); - } - } - } - svars->state[t] |= ST_SENT_FIND_OLD; - msgs_found_sel( svars, t ); -} - -static void -msg_found_sel( int sts, int uid, void *aux ) -{ - SVARS_CHECK_RET_VARS(find_vars_t); - 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 ); - msgs_found_sel( svars, t ); + DRIVER_CALL_RET(load( svars->ctx[t], minwuid, maxwuid, svars->uidnext[t], mexcs, nmexcs, box_loaded, AUX )); } typedef struct { @@ -1021,8 +998,9 @@ static void msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_ static void msgs_copied( sync_vars_t *svars, int t ); static void -msgs_found_sel( sync_vars_t *svars, int t ) +box_loaded( int sts, void *aux ) { + DECL_SVARS; sync_rec_t *srec, *nsrec = 0; message_t *tmsg; copy_vars_t *cv; @@ -1032,8 +1010,18 @@ 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]) + if (check_ret( sts, aux )) return; + INIT_SVARS(aux); + svars->state[t] |= ST_LOADED; + 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. @@ -1043,6 +1031,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 ); @@ -1133,7 +1123,7 @@ msgs_found_sel( sync_vars_t *svars, int t ) return; } - 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_LOADED)) return; if (svars->uidval[M] < 0 || svars->uidval[S] < 0) { @@ -1369,6 +1359,8 @@ msg_copied( int sts, int uid, copy_vars_t *vars ) SVARS_CHECK_CANCEL_RET; 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: @@ -1405,55 +1397,45 @@ msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int } } -static void msg_found_new( int sts, int uid, void *aux ); +static void msgs_found_new( int sts, void *aux ); +static void msgs_new_done( sync_vars_t *svars, int t ); static void sync_close( sync_vars_t *svars, int t ); 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; - 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; - DRIVER_CALL(find_msg( svars->ctx[t], srec->tuid, msg_found_new, fv )); - } + if (svars->state[t] & S_FIND) { + debug( "finding just copied messages on %s\n", str_ms[t] ); + svars->drv[t]->find_new_msgs( svars->ctx[t], msgs_found_new, AUX ); + } else { + msgs_new_done( svars, t ); } - svars->state[t] |= ST_SENT_FIND_NEW; - sync_close( svars, t ); } static void -msg_found_new( int sts, int uid, void *aux ) +msgs_found_new( int sts, void *aux ) { - SVARS_CHECK_RET_VARS(find_vars_t); + SVARS_CHECK_RET; 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 ); + msgs_new_done( svars, t ); +} + +static void +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; sync_close( svars, t ); } @@ -1614,8 +1596,7 @@ static void box_closed_p2( sync_vars_t *svars, int t ); 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] || + if ((~svars->state[t] & (ST_FOUND_NEW|ST_SENT_TRASH)) || svars->trash_done[t] < svars->trash_total[t]) return; @@ -1686,7 +1667,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; ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel