commit 42f165ecf72028bd5b41211537d2a64e117be82f
Author: Oswald Buddenhagen <o...@users.sf.net>
Date:   Wed Aug 5 19:48:58 2020 +0200

    fix UIDNEXT query vs. concurrent imap_fetch_msg()
    
    the uidnext query following message stores can be interleaved with
    message fetches. that means that we cannot rely on the 1st command in
    flight being that query. but instead of iterating over all commands in
    flight, move the uidnext query flag to imap_store (and make sure to
    check for the presence of a message body before testing it) - this
    avoids the loop and an extra byte in every command.
    
    this also makes it clear that the query is mutually exclusive with
    loading messages (the untagged responses are not distinguishable).

 src/drv_imap.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/drv_imap.c b/src/drv_imap.c
index 27d43ec..054face 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -113,6 +113,8 @@ struct imap_store {
        enum { SST_BAD, SST_HALF, SST_GOOD } state;
        /* trash folder's existence is not confirmed yet */
        enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
+       // What kind of BODY-less FETCH response we're expecting.
+       enum { FetchNone, FetchMsgs, FetchUidNext } fetch_sts;
        uint got_namespace:1;
        uint has_forwarded:1;
        char delimiter[2]; /* hierarchy delimiter */
@@ -174,7 +176,6 @@ struct imap_cmd {
                char to_trash; /* we are storing to trash, not current. */
                char create; /* create the mailbox if we get an error which 
suggests so. */
                char failok; /* Don't complain about NO response. */
-               char lastuid; /* querying the last UID in the mailbox. */
        } param;
 };
 
@@ -1144,15 +1145,11 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char 
*s ATTR_UNUSED )
        if (!uid) {
                // Ignore async flag updates for now.
                status &= ~(M_FLAGS | M_RECENT);
-       } else if ((cmdp = ctx->in_progress) && cmdp->param.lastuid) {
-               // Workaround for server not sending UIDNEXT and/or APPENDUID.
-               ctx->uidnext = uid + 1;
        } else if (status & M_BODY) {
                for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next)
                        if (cmdp->param.uid == uid)
                                goto gotuid;
-               error( "IMAP error: unexpected FETCH response (UID %u)\n", uid 
);
-               return LIST_BAD;
+               goto badrsp;
          gotuid:
                msgdata = ((imap_cmd_fetch_msg_t *)cmdp)->msg_data;
                msgdata->data = body->val;
@@ -1162,7 +1159,10 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char 
*s ATTR_UNUSED )
                if (status & M_FLAGS)
                        msgdata->flags = mask;
                status &= ~(M_FLAGS | M_RECENT | M_BODY | M_DATE);
-       } else {
+       } else if (ctx->fetch_sts == FetchUidNext) {
+               // Workaround for server not sending UIDNEXT and/or APPENDUID.
+               ctx->uidnext = uid + 1;
+       } else if (ctx->fetch_sts == FetchMsgs) {
                cur = nfcalloc( sizeof(*cur) );
                *ctx->msgapp = &cur->gen;
                ctx->msgapp = &cur->gen.next;
@@ -1175,6 +1175,10 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char 
*s ATTR_UNUSED )
                if (tuid)
                        memcpy( cur->gen.tuid, tuid, TUIDL );
                status &= ~(M_FLAGS | M_RECENT | M_SIZE | M_HEADER);
+       } else {
+         badrsp:
+               error( "IMAP error: unexpected FETCH response (UID %u)\n", uid 
);
+               return LIST_BAD;
        }
 
        if (status) {
@@ -2536,8 +2540,9 @@ imap_open_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, 
int response )
                return;
        }
 
+       assert( ctx->fetch_sts == FetchNone );
+       ctx->fetch_sts = FetchUidNext;
        INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cmdp->callback, 
cmdp->callback_aux)
-       cmd->gen.param.lastuid = 1;
        imap_exec( ctx, &cmd->gen, imap_open_box_p3,
                   "UID FETCH * (UID)" );
 }
@@ -2547,6 +2552,7 @@ imap_open_box_p3( imap_store_t *ctx, imap_cmd_t *gcmd, 
int response )
 {
        imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd;
 
+       ctx->fetch_sts = FetchNone;
        if (!ctx->uidnext) {
                if (ctx->total_msgs) {
                        error( "IMAP error: querying server for highest UID 
failed\n" );
@@ -2714,6 +2720,9 @@ imap_load_box( store_t *gctx, uint minuid, uint maxuid, 
uint finduid, uint pairu
                free( excs.data );
                cb( DRV_OK, NULL, 0, 0, aux );
        } else {
+               assert( ctx->fetch_sts == FetchNone );
+               ctx->fetch_sts = FetchMsgs;
+
                INIT_REFCOUNTED_STATE(imap_load_box_state_t, sts, cb, aux)
                for (uint i = 0; i < excs.size; ) {
                        for (int bl = 0; i < excs.size && bl < 960; i++) {
@@ -2821,6 +2830,7 @@ static void
 imap_submit_load_p3( imap_store_t *ctx, imap_load_box_state_t *sts )
 {
        DONE_REFCOUNTED_STATE_ARGS(sts, {
+               ctx->fetch_sts = FetchNone;
                if (sts->gen.ret_val == DRV_OK)
                        imap_sort_msgs( ctx );
        }, ctx->msgs, ctx->total_msgs, ctx->recent_msgs)
@@ -3126,11 +3136,12 @@ imap_find_new_msgs_p2( imap_store_t *ctx, imap_cmd_t 
*gcmd, int response )
 
        // We appended messages, so we need to re-query UIDNEXT.
        ctx->uidnext = 0;
+       assert( ctx->fetch_sts == FetchNone );
+       ctx->fetch_sts = FetchUidNext;
 
        INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cmdp->callback, 
cmdp->callback_aux)
        cmd->out_msgs = cmdp->out_msgs;
        cmd->uid = cmdp->uid;
-       cmd->gen.param.lastuid = 1;
        imap_exec( ctx, &cmd->gen, imap_find_new_msgs_p3,
                   "UID FETCH * (UID)" );
 }
@@ -3141,6 +3152,7 @@ imap_find_new_msgs_p3( imap_store_t *ctx, imap_cmd_t 
*gcmd, int response )
        imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd;
        imap_cmd_find_new_t *cmd;
 
+       ctx->fetch_sts = FetchNone;
        if (response != RESP_OK) {
                imap_find_new_msgs_p4( ctx, gcmd, response );
                return;


_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to