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

Reply via email to