isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit 0f24ca31b5fafe5228d0e99f460f1c823121b296
parent b2f6ef391bba6c0c77002f73fe259b424fce1169
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Fri,  4 Nov 2016 16:21:09 +0100

fix SubFolders style Maildir++

turns out i misread the spec in a subtle way: while all other folders
are physically nested under INBOX, the IMAP view puts them at the same
(root) level. to get them shown as subfolders of INBOX, they need to
have _two_ leading dots.

this also implies that the Maildir++ mode has no use for a Path, so
reject attempts to specify one.

Diffstat:
Msrc/drv_maildir.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/mbsync.1 | 13+++++++------
2 files changed, 89 insertions(+), 51 deletions(-)

diff --git a/src/drv_maildir.c b/src/drv_maildir.c @@ -113,13 +113,34 @@ maildir_parse_flags( const char *info_prefix, const char *base ) return flags; } +static int +maildir_ensure_path( maildir_store_conf_t *conf ) +{ + if (!conf->gen.path) { + error( "Maildir error: store '%s' has no Path\n", conf->gen.name ); + conf->failed = FAIL_FINAL; + return -1; + } + return 0; +} + +/* Subdirs of INBOX include a leading slash. */ +/* Path includes a trailing slash, Inbox does not. */ static char * -maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char *box ) +maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box ) { char *out, *p; - int pl, bl, n, sub = 0; + const char *prefix; + int pl, bl, n; char c; + if (in_inbox || conf->sub_style == SUB_MAILDIRPP) { + prefix = conf->inbox; + } else { + if (maildir_ensure_path( conf ) < 0) + return 0; + prefix = conf->gen.path; + } pl = strlen( prefix ); for (bl = 0, n = 0; (c = box[bl]); bl++) if (c == '/') { @@ -134,24 +155,33 @@ maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char *b conf->gen.name, box ); return 0; } + switch (conf->sub_style) { + case SUB_VERBATIM: + n = 0; + break; + case SUB_MAILDIRPP: + n = 2; + break; + default: /* SUB_LEGACY and SUB_UNSET */ + break; + } out = nfmalloc( pl + bl + n + 1 ); memcpy( out, prefix, pl ); p = out + pl; + if (conf->sub_style == SUB_MAILDIRPP) { + *p++ = '/'; + *p++ = '.'; + } while ((c = *box++)) { 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; + /* fallthrough */ + default: /* SUB_MAILDIRPP */ *p++ = '.'; break; } @@ -164,17 +194,6 @@ maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char *b } static int -maildir_ensure_path( maildir_store_conf_t *conf ) -{ - if (!conf->gen.path) { - error( "Maildir error: store '%s' has no Path\n", conf->gen.name ); - conf->failed = FAIL_FINAL; - return -1; - } - return 0; -} - -static int maildir_validate_path( maildir_store_conf_t *conf ) { struct stat st; @@ -212,15 +231,9 @@ maildir_connect_store( store_t *gctx, cb( DRV_STORE_BAD, aux ); return; } - if (conf->gen.trash) { - if (maildir_ensure_path( conf ) < 0) { - cb( DRV_STORE_BAD, aux ); - return; - } - if (!(ctx->trash = maildir_join_path( conf, conf->gen.path, conf->gen.trash ))) { - cb( DRV_STORE_BAD, aux ); - return; - } + if (conf->gen.trash && !(ctx->trash = maildir_join_path( conf, 0, conf->gen.trash ))) { + cb( DRV_STORE_BAD, aux ); + return; } cb( DRV_OK, aux ); } @@ -325,6 +338,8 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags, return -1; } } else { + char *effName = name; + int nameOff = 0; if (style == SUB_MAILDIRPP || style == SUB_LEGACY) { if (*ent == '.') { if (!isBox) @@ -334,15 +349,27 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags, if (isBox) continue; } + if (style == SUB_MAILDIRPP) { + if (*ent == '.') { + if (!(flags & LIST_INBOX)) + continue; + ent++; + } else { + if (!(flags & (LIST_PATH | LIST_PATH_MAYBE))) + continue; + effName = name + 6; + nameOff = 6; + } + } } nl = nameLen + nfsnprintf( name + nameLen, _POSIX_PATH_MAX - nameLen, "%s", ent ); - if (style == SUB_MAILDIRPP && isBox) { + if (style == SUB_MAILDIRPP) { for (i = nameLen; i < nl; i++) { if (name[i] == '.') name[i] = '/'; } } - if (!nameLen && equals( name, nl, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { + if (nameLen == nameOff && equals( effName, nl - nameOff, "INBOX", 5 ) && (!effName[5] || effName[5] == '/')) { path[pathLen] = 0; warn( "Maildir warning: ignoring INBOX in %s\n", path ); continue; @@ -350,9 +377,9 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags, 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. */ + add_string_list( &gctx->boxes, effName ); + if (style == SUB_MAILDIRPP) { + /* Maildir++ folder - don't recurse further. */ continue; } path[pl] = 0; @@ -372,7 +399,8 @@ maildir_list_inbox( store_t *gctx, int flags, const char *basePath ) { char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX]; - add_string_list( &gctx->boxes, "INBOX" ); + if (flags & LIST_INBOX) + add_string_list( &gctx->boxes, "INBOX" ); return maildir_list_recurse( gctx, 1, flags, 0, 0, basePath, basePath ? strlen( basePath ) - 1 : 0, path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ((maildir_store_conf_t *)gctx->conf)->inbox ), @@ -396,9 +424,12 @@ static void maildir_list_store( store_t *gctx, int flags, void (*cb)( int sts, void *aux ), void *aux ) { - if ((((flags & LIST_PATH) || ((flags & LIST_PATH_MAYBE) && gctx->conf->path)) - && maildir_list_path( gctx, flags, ((maildir_store_conf_t *)gctx->conf)->inbox ) < 0) || - ((flags & LIST_INBOX) && maildir_list_inbox( gctx, flags, gctx->conf->path ) < 0)) { + maildir_store_conf_t *conf = (maildir_store_conf_t *)gctx->conf; + if (((((flags & LIST_PATH) && conf->sub_style != SUB_MAILDIRPP) + || ((flags & LIST_PATH_MAYBE) && gctx->conf->path)) + && maildir_list_path( gctx, flags, conf->inbox ) < 0) || + (((flags & LIST_INBOX) || conf->sub_style == SUB_MAILDIRPP) + && maildir_list_inbox( gctx, flags, gctx->conf->path ) < 0)) { maildir_invoke_bad_callback( gctx ); cb( DRV_CANCELED, aux ); } else { @@ -1111,14 +1142,16 @@ 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, conf->inbox, name + 5 ); - ctx->is_inbox = !name[5]; - } else { - if (maildir_ensure_path( conf ) < 0) { - gctx->path = 0; - return DRV_CANCELED; + if (!name[5]) { + gctx->path = nfstrdup( conf->inbox ); + ctx->is_inbox = 1; + } else { + gctx->path = maildir_join_path( conf, 1, name + 5 ); + ctx->is_inbox = 0; } - gctx->path = maildir_join_path( conf, conf->gen.path, name ); + } else { + if (!(gctx->path = maildir_join_path( conf, 0, name ))) + return DRV_CANCELED; ctx->is_inbox = 0; } return gctx->path ? DRV_OK : DRV_BOX_BAD; @@ -1741,6 +1774,10 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep ) parse_generic_store( &store->gen, cfg ); if (!store->inbox) store->inbox = expand_strdup( "~/Maildir" ); + if (store->sub_style == SUB_MAILDIRPP && store->gen.path) { + error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->gen.name ); + cfg->err = 1; + } nfasprintf( &store->info_prefix, "%c2,", store->info_delimiter ); nfasprintf( &store->info_stop, "%c,", store->info_delimiter ); *storep = &store->gen; diff --git a/src/mbsync.1 b/src/mbsync.1 @@ -264,17 +264,18 @@ with DOS/Windows file systems. 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: +Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and +\fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths: .br -\fBVerbatim\fR - \fItop/sub/subsub\fR +\fBVerbatim\fR - \fIPath\fB/top/sub/subsub\fR and \fIInbox\fB/sub/subsub\fR (this is the style you probably want to use) .br -\fBMaildir++\fR - \fItop/.sub.subsub\fR +\fBMaildir++\fR - \fIInbox\fB/.top.sub.subsub\fR and \fIInbox\fB/..sub.subsub\fR (this style is compatible with Courier and Dovecot - but note that -the mailbox metadata format is \fInot\fR compatible) +the mailbox metadata format is \fInot\fR compatible). +Note that attempts to set \fBPath\fR are rejected in this mode. .br -\fBLegacy\fR - \fItop/.sub/.subsub\fR +\fBLegacy\fR - \fIPath\fB/top/.sub/.subsub\fR and \fIInbox\fB/.sub/.subsub\fR (this is \fBmbsync\fR's historical style) .br (Default: unset; will error out when sub-folders are encountered)