commit 549e6739e82ef2428e92a15cf455edc64ad77579
Author: Oswald Buddenhagen <o...@users.sf.net>
Date:   Fri May 1 19:16:23 2015 +0200

    support verbatim and real Maildir++ subfolder naming styles
    
    the legacy style is a poorly executed attempt at Maildir++, so introduce
    the latter for the sake of completeness. but most users will probably
    just want to use subfolders without any additional dots.

 NEWS                |    2 +
 src/drv_maildir.c   |  126 ++++++++++++++++++++++++++++++++----------
 src/mbsync.1        |   20 +++++++
 src/mbsyncrc.sample |    1 +
 src/sync.c          |    6 ++-
 5 files changed, 124 insertions(+), 31 deletions(-)

diff --git a/NEWS b/NEWS
index f0d2743..5ba9777 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@
 
 Network timeout handling has been added.
 
+A Maildir sub-folder naming style without extra dots has been added.
+
 [1.2.0]
 
 The 'isync' compatibility wrapper is now deprecated.
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index 509084f..59a40d2 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -50,6 +50,11 @@
 #include <db.h>
 #endif /* USE_DB */
 
+#define SUB_UNSET      0
+#define SUB_VERBATIM   1
+#define SUB_MAILDIRPP  2
+#define SUB_LEGACY     3
+
 typedef struct maildir_store_conf {
        store_conf_t gen;
        char *inbox;
@@ -57,6 +62,7 @@ typedef struct maildir_store_conf {
        int alt_map;
 #endif /* USE_DB */
        char info_delimiter;
+       char sub_style;
        char failed;
        char *info_prefix, *info_stop; /* precalculated from info_delimiter */
 } maildir_store_conf_t;
@@ -113,23 +119,50 @@ maildir_parse_flags( const char *info_prefix, const char 
*base )
 }
 
 static char *
-maildir_join_path( const char *prefix, const char *box )
+maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char 
*box )
 {
        char *out, *p;
-       int pl, bl, n;
+       int pl, bl, n, sub = 0;
        char c;
 
        pl = strlen( prefix );
        for (bl = 0, n = 0; (c = box[bl]); bl++)
-               if (c == '/')
+               if (c == '/') {
+                       if (conf->sub_style == SUB_UNSET) {
+                               error( "Maildir error: accessing subfolder 
'%s', but store '%s' does not specify SubFolders style\n",
+                                      box, conf->gen.name );
+                               return 0;
+                       }
                        n++;
+               } else if (c == '.' && conf->sub_style == SUB_MAILDIRPP) {
+                       error( "Maildir error: store '%s', folder '%s': 
SubFolders style Maildir++ does not support dots in mailbox names\n",
+                              conf->gen.name, box );
+                       return 0;
+               }
        out = nfmalloc( pl + bl + n + 1 );
        memcpy( out, prefix, pl );
        p = out + pl;
        while ((c = *box++)) {
-               *p++ = c;
-               if (c == '/')
-                       *p++ = '.';
+               if (c == '/') {
+                       switch (conf->sub_style) {
+                       case SUB_VERBATIM:
+                               *p++ = c;
+                               break;
+                       case SUB_MAILDIRPP:
+                               if (!sub) {
+                                       sub = 1;
+                                       *p++ = c;
+                               }
+                               *p++ = '.';
+                               break;
+                       case SUB_LEGACY:
+                               *p++ = c;
+                               *p++ = '.';
+                               break;
+                       }
+               } else {
+                       *p++ = c;
+               }
        }
        *p = 0;
        return out;
@@ -172,7 +205,11 @@ maildir_open_store( store_conf_t *gconf, const char *label 
ATTR_UNUSED,
                        cb( 0, aux );
                        return;
                }
-               ctx->trash = maildir_join_path( gconf->path, gconf->trash );
+               if (!(ctx->trash = maildir_join_path( conf, gconf->path, 
gconf->trash ))) {
+                       free( ctx );
+                       cb( 0, aux );
+                       return;
+               }
        }
        cb( &ctx->gen, aux );
 }
@@ -239,7 +276,8 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
                       char *path, int pathLen, char *name, int nameLen )
 {
        DIR *dir;
-       int pl, nl;
+       int style = ((maildir_store_conf_t *)gctx->conf)->sub_style;
+       int pl, nl, i;
        struct dirent *de;
        struct stat st;
 
@@ -249,9 +287,20 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
                sys_error( "Maildir error: cannot list %s", path );
                return -1;
        }
+       if (isBox > 1 && style == SUB_UNSET) {
+               error( "Maildir error: found subfolder '%.*s', but store '%s' 
does not specify SubFolders style\n",
+                      nameLen - 1, name, gctx->conf->name );
+               closedir( dir );
+               return -1;
+       }
        while ((de = readdir( dir ))) {
                const char *ent = de->d_name;
-               pl = pathLen + nfsnprintf( path + pathLen, _POSIX_PATH_MAX - 
pathLen, "%s", ent );
+               if (ent[0] == '.' && (!ent[1] || (ent[1] == '.' && !ent[2])))
+                       continue;
+               pl = nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, 
"%s", ent );
+               if (pl == 3 && (!memcmp( ent, "cur", 3 ) || !memcmp( ent, 
"new", 3 ) || !memcmp( ent, "tmp", 3 )))
+                       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) {
@@ -265,29 +314,39 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
                                return -1;
                        }
                } else {
-                       if (*ent == '.') {
-                               if (!isBox)
-                                       continue;
-                               if (!ent[1] || ent[1] == '.')
-                                       continue;
-                               ent++;
-                       } else {
-                               if (isBox)
-                                       continue;
-                               if (!nameLen && equals( ent, -1, "INBOX", 5 )) {
-                                       path[pathLen] = 0;
-                                       warn( "Maildir warning: ignoring INBOX 
in %s\n", path );
-                                       continue;
+                       if (style == SUB_MAILDIRPP || style == SUB_LEGACY) {
+                               if (*ent == '.') {
+                                       if (!isBox)
+                                               continue;
+                                       ent++;
+                               } else {
+                                       if (isBox)
+                                               continue;
                                }
                        }
                        nl = nameLen + nfsnprintf( name + nameLen, 
_POSIX_PATH_MAX - nameLen, "%s", ent );
+                       if (style == SUB_MAILDIRPP && isBox) {
+                               for (i = nameLen; i < nl; i++) {
+                                       if (name[i] == '.')
+                                               name[i] = '/';
+                               }
+                       }
+                       if (!nameLen && equals( name, nl, "INBOX", 5 ) && 
(!name[5] || name[5] == '/')) {
+                               path[pathLen] = 0;
+                               warn( "Maildir warning: ignoring INBOX in 
%s\n", path );
+                               continue;
+                       }
                        path[pl++] = '/';
                        nfsnprintf( path + pl, _POSIX_PATH_MAX - pl, "cur" );
                        if (!stat( path, &st ) && S_ISDIR(st.st_mode))
                                add_string_list( &gctx->boxes, name );
+                       if (style == SUB_MAILDIRPP && isBox) {
+                               /* Maildir++ subfolder - don't recurse further. 
*/
+                               continue;
+                       }
                        path[pl] = 0;
                        name[nl++] = '/';
-                       if (maildir_list_recurse( gctx, 1, flags, inbox, 
inboxLen, basePath, basePathLen, path, pl, name, nl ) < 0) {
+                       if (maildir_list_recurse( gctx, isBox + 1, flags, 
inbox, inboxLen, basePath, basePathLen, path, pl, name, nl ) < 0) {
                                closedir( dir );
                                return -1;
                        }
@@ -404,10 +463,6 @@ make_box_dir( char *buf, int bl )
        if (!mkdir( buf, 0700 ) || errno == EEXIST)
                return 0;
        p = memrchr( buf, '/', bl - 1 );
-       if (*(p + 1) != '.') {
-               errno = ENOENT;
-               return -1;
-       }
        *p = 0;
        if (make_box_dir( buf, (int)(p - buf) ))
                return -1;
@@ -1055,17 +1110,17 @@ maildir_select_box( store_t *gctx, const char *name )
 #endif /* USE_DB */
        ctx->fresh[0] = ctx->fresh[1] = 0;
        if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == 
'/')) {
-               gctx->path = maildir_join_path( conf->inbox, name + 5 );
+               gctx->path = maildir_join_path( conf, conf->inbox, name + 5 );
                ctx->is_inbox = !name[5];
        } else {
                if (maildir_validate_path( conf ) < 0) {
                        gctx->path = 0;
                        return DRV_CANCELED;
                }
-               gctx->path = maildir_join_path( gctx->conf->path, name );
+               gctx->path = maildir_join_path( conf, conf->gen.path, name );
                ctx->is_inbox = 0;
        }
-       return DRV_OK;
+       return gctx->path ? DRV_OK : DRV_BOX_BAD;
 }
 
 static void
@@ -1670,6 +1725,17 @@ maildir_parse_store( conffile_t *cfg, store_conf_t 
**storep )
                                cfg->err = 1;
                                continue;
                        }
+               } else if (!strcasecmp( "SubFolders", cfg->cmd )) {
+                       if (!strcasecmp( "Verbatim", cfg->val )) {
+                               store->sub_style = SUB_VERBATIM;
+                       } else if (!strcasecmp( "Maildir++", cfg->val )) {
+                               store->sub_style = SUB_MAILDIRPP;
+                       } else if (!strcasecmp( "Legacy", cfg->val )) {
+                               store->sub_style = SUB_LEGACY;
+                       } else {
+                               error( "%s:%d: Unrecognized SubFolders 
style\n", cfg->file, cfg->line );
+                               cfg->err = 1;
+                       }
                } else
                        parse_generic_store( &store->gen, cfg );
        if (!store->inbox)
diff --git a/src/mbsync.1 b/src/mbsync.1
index 2f1d16f..66b95aa 100644
--- a/src/mbsync.1
+++ b/src/mbsync.1
@@ -256,6 +256,26 @@ The Maildir standard defines this to be the colon, but 
this is incompatible
 with DOS/Windows file systems.
 (Default: the value of \fBFieldDelimiter\fR)
 ..
+.TP
+\fBSubFolders\fR \fBVerbatim\fR|\fBMaildir++\fR|\fBLegacy\fR
+The on-disk folder naming style used for hierarchical mailboxes.
+This has option has no effect when \fBFlatten\fR is used.
+.br
+Suppose a mailbox with the canonical path \fItop/sub/subsub\fR,
+the styles will yield the following on-disk paths:
+.br
+\fBVerbatim\fR - \fItop/sub/subsub\fR
+(this is the style you probably want to use)
+.br
+\fBMaildir++\fR - \fItop/.sub.subsub\fR
+(this style is compatible with Courier and Dovecot - but note that
+the mailbox metadata format is \fInot\fR compatible)
+.br
+\fBLegacy\fR - \fItop/.sub/.subsub\fR
+(this is \fBmbsync\fR's historical style)
+.br
+(Default: unset; will error out when sub-folders are encountered)
+..
 .SS IMAP4 Accounts
 .TP
 \fBIMAPAccount\fR \fIname\fR
diff --git a/src/mbsyncrc.sample b/src/mbsyncrc.sample
index c7d61c7..d7c5b53 100644
--- a/src/mbsyncrc.sample
+++ b/src/mbsyncrc.sample
@@ -87,6 +87,7 @@ TrashRemoteNew yes
 
 MaildirStore mirror
 Path ~/Maildir/
+SubFolders Verbatim
 
 Channel o2o
 Master :server:
diff --git a/src/sync.c b/src/sync.c
index eb444e0..9672734 100644
--- a/src/sync.c
+++ b/src/sync.c
@@ -967,6 +967,7 @@ sync_boxes( store_t *ctx[], const char *names[], int 
present[], channel_conf_t *
                        svars->box_name[t] = nfstrdup( svars->orig_name[t] );
                } else if (map_name( svars->orig_name[t], &svars->box_name[t], 
0, "/", ctx[t]->conf->flat_delim ) < 0) {
                        error( "Error: canonical mailbox name '%s' contains 
flattened hierarchy delimiter\n", svars->orig_name[t] );
+                 bail3:
                        svars->ret = SYNC_FAIL;
                        sync_bail3( svars );
                        return;
@@ -978,9 +979,12 @@ sync_boxes( store_t *ctx[], const char *names[], int 
present[], channel_conf_t *
        /* Both boxes must be fully set up at this point, so that error exit 
paths
         * don't run into uninitialized variables. */
        for (t = 0; t < 2; t++) {
-               if (svars->drv[t]->select_box( ctx[t], svars->box_name[t] ) == 
DRV_CANCELED) {
+               switch (svars->drv[t]->select_box( ctx[t], svars->box_name[t] 
)) {
+               case DRV_CANCELED:
                        store_bad( AUX );
                        return;
+               case DRV_BOX_BAD:
+                       goto bail3;
                }
        }
 

------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud 
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to