commit 416ced25ddff3bc1e57720164978d805c83a9d3a
Author: Oswald Buddenhagen <o...@users.sf.net>
Date:   Tue Mar 21 18:46:30 2017 +0100

    fix repeated listing of same Store with different flags
    
    multiple Channels can call driver_t::list_store() with different LIST_*
    flags. assuming the flags are actually taken into consideration, using a
    single boolean 'listed' flag to track whether the Store still needs to
    be listed obviously wouldn't cut it - if INBOX does not live right under
    Path and the Channels used entirely disjoint Patterns (say, * and
    INBOX*), the second Channel in a single run (probably a Group) would
    fail to match anything.
    
    to fix this, make store_t::listed more granular. this also requires
    moving its handling back into the drivers (thus reverting c66afdc0),
    because the actually performed queries and their possible implicit
    results are driver-specific.
    
    note that this slightly pessimizes some cases - e.g., an IMAP Store with
    Path "" will now list the entire namespace even if there is only one
    Channel with Pattern "INBOX*" (because a hypothetical Pattern "*" would
    also include INBOX*, and the queries are kept disjoint to avoid the need
    for de-duplication). this isn't expected to be a problem, as listing
    mailboxes is generally cheap.

 src/drv_imap.c    | 30 +++++++++++++++++++++---------
 src/drv_maildir.c | 28 +++++++++++++++++++++++-----
 src/main.c        |  3 +--
 3 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/src/drv_imap.c b/src/drv_imap.c
index e12c7cc..adffc38 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -2759,23 +2759,35 @@ imap_list_store( store_t *gctx, int flags,
        imap_cmd_refcounted_state_t *sts = imap_refcounted_new_state( cb, aux );
 
        // ctx->prefix may be empty, "INBOX.", or something else.
-       // 'flags' may be LIST_INBOX, LIST_PATH (or LIST_PATH_MAYBE), or both.
-       // This matrix determines what to query, and what comes out as a side 
effect:
+       // 'flags' may be LIST_INBOX, LIST_PATH (or LIST_PATH_MAYBE), or both. 
'listed'
+       // already containing a particular value effectively removes it from 
'flags'.
+       // This matrix determines what to query, and what comes out as a side 
effect.
+       // The lowercase letters indicate unnecessary results; the queries are 
done
+       // this way to have non-overlapping result sets, so subsequent calls 
create
+       // no duplicates:
        //
        // qry \ pfx | empty | inbox | other
        // ----------+-------+-------+-------
-       // inbox     | i     | i [p] | i
-       // both      | p [i] | i [p] | i + p
-       // path      | p [i] | p {i} | p
+       // inbox     | p [I] | I [p] | I
+       // both      | P [I] | I [P] | I + P
+       // path      | P [i] | i [P] | P
        //
-       // {i} => This doesn't actually contain INBOX itself, only its 
subfolders.
-       //
-       if ((flags & (LIST_PATH | LIST_PATH_MAYBE)) && (!(flags & LIST_INBOX) 
|| !is_inbox( ctx, ctx->prefix, -1 )))
+       int pfx_is_empty = !*ctx->prefix;
+       int pfx_is_inbox = !pfx_is_empty && is_inbox( ctx, ctx->prefix, -1 );
+       if (((flags & (LIST_PATH | LIST_PATH_MAYBE)) || pfx_is_empty) && 
!pfx_is_inbox && !(ctx->gen.listed & LIST_PATH)) {
+               ctx->gen.listed |= LIST_PATH;
+               if (pfx_is_empty)
+                       ctx->gen.listed |= LIST_INBOX;
                imap_exec( ctx, imap_refcounted_new_cmd( sts ), 
imap_refcounted_done_box,
                           "LIST \"\" \"%\\s*\"", ctx->prefix );
-       if ((flags & LIST_INBOX) && (!(flags & (LIST_PATH | LIST_PATH_MAYBE)) 
|| *ctx->prefix))
+       }
+       if (((flags & LIST_INBOX) || pfx_is_inbox) && !pfx_is_empty && 
!(ctx->gen.listed & LIST_INBOX)) {
+               ctx->gen.listed |= LIST_INBOX;
+               if (pfx_is_inbox)
+                       ctx->gen.listed |= LIST_PATH;
                imap_exec( ctx, imap_refcounted_new_cmd( sts ), 
imap_refcounted_done_box,
                           "LIST \"\" INBOX*" );
+       }
        imap_refcounted_done( sts );
 }
 
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index 15b7e2e..d95d522 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -300,7 +300,11 @@ maildir_list_maildirpp( store_t *gctx, int flags, const 
char *inbox )
        int warned = 0;
        struct stat st;
 
-       add_string_list( &gctx->boxes, "INBOX" );
+       if (gctx->listed & LIST_PATH)  // Implies LIST_INBOX
+               return 0;
+
+       if (!(gctx->listed & LIST_INBOX))
+               add_string_list( &gctx->boxes, "INBOX" );
 
        char path[_POSIX_PATH_MAX];
        int pathLen = nfsnprintf( path, _POSIX_PATH_MAX, "%s/", inbox );
@@ -317,6 +321,8 @@ maildir_list_maildirpp( store_t *gctx, int flags, const 
char *inbox )
                char name[_POSIX_PATH_MAX];
                char *effName = name;
                if (*ent == '.') {
+                       if (gctx->listed & LIST_INBOX)
+                               continue;
                        if (!*++ent)
                                continue;
                        // The Maildir++ Inbox is technically not under Path 
(as there is none), so
@@ -346,6 +352,10 @@ maildir_list_maildirpp( store_t *gctx, int flags, const 
char *inbox )
                }
        }
        closedir (dir);
+
+       if (flags & (LIST_PATH | LIST_PATH_MAYBE))
+               gctx->listed |= LIST_PATH;
+       gctx->listed |= LIST_INBOX;
        return 0;
 }
 
@@ -384,14 +394,14 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
                        continue;
                pl += pathLen;
                if (inbox && equals( path, pl, inbox, inboxLen )) {
-                       /* Inbox nested into Path. List now if it won't be 
listed separately anyway. */
-                       if (!(flags & LIST_INBOX) && maildir_list_inbox( gctx, 
flags, 0 ) < 0) {
+                       // Inbox nested into Path.
+                       if (maildir_list_inbox( gctx, flags, 0 ) < 0) {
                                closedir( dir );
                                return -1;
                        }
                } else if (basePath && equals( path, pl, basePath, basePathLen 
)) {
-                       /* Path nested into Inbox. List now if it won't be 
listed separately anyway. */
-                       if (!(flags & (LIST_PATH | LIST_PATH_MAYBE)) && 
maildir_list_path( gctx, flags, 0 ) < 0) {
+                       // Path nested into Inbox.
+                       if (maildir_list_path( gctx, flags, 0 ) < 0) {
                                closedir( dir );
                                return -1;
                        }
@@ -433,6 +443,10 @@ maildir_list_inbox( store_t *gctx, int flags, const char 
*basePath )
 {
        char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
 
+       if (gctx->listed & LIST_INBOX)
+               return 0;
+       gctx->listed |= LIST_INBOX;
+
        add_string_list( &gctx->boxes, "INBOX" );
        return maildir_list_recurse(
                gctx, 1, flags, 0, 0, basePath, basePath ? strlen( basePath ) - 
1 : 0,
@@ -445,6 +459,10 @@ maildir_list_path( store_t *gctx, int flags, const char 
*inbox )
 {
        char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
 
+       if (gctx->listed & LIST_PATH)
+               return 0;
+       gctx->listed |= LIST_PATH;
+
        if (maildir_ensure_path( (maildir_store_conf_t *)gctx->conf ) < 0)
                return -1;
        return maildir_list_recurse(
diff --git a/src/main.c b/src/main.c
index b5a39fb..a85ca22 100644
--- a/src/main.c
+++ b/src/main.c
@@ -945,7 +945,7 @@ store_connected( int sts, void *aux )
        case DRV_CANCELED:
                return;
        case DRV_OK:
-               if (!mvars->skip && !mvars->chanptr->boxlist && 
mvars->chan->patterns && !mvars->ctx[t]->listed) {
+               if (!mvars->skip && !mvars->chanptr->boxlist && 
mvars->chan->patterns) {
                        for (cflags = 0, cpat = mvars->chan->patterns; cpat; 
cpat = cpat->next) {
                                const char *pat = cpat->string;
                                if (*pat != '!') {
@@ -1004,7 +1004,6 @@ store_listed( int sts, void *aux )
        case DRV_CANCELED:
                return;
        case DRV_OK:
-               mvars->ctx[t]->listed = 1;
                if (DFlags & DEBUG_MAIN) {
                        debug( "got mailbox list from %s:\n", str_ms[t] );
                        for (bx = mvars->ctx[t]->boxes; bx; bx = bx->next)

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to