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.
This commit is contained in:
Oswald Buddenhagen 2016-11-04 16:21:09 +01:00
parent b2f6ef391b
commit 0f24ca31b5
2 changed files with 89 additions and 51 deletions

View File

@ -113,13 +113,34 @@ maildir_parse_flags( const char *info_prefix, const char *base )
return flags; 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 * 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; char *out, *p;
int pl, bl, n, sub = 0; const char *prefix;
int pl, bl, n;
char c; 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 ); pl = strlen( prefix );
for (bl = 0, n = 0; (c = box[bl]); bl++) for (bl = 0, n = 0; (c = box[bl]); bl++)
if (c == '/') { if (c == '/') {
@ -134,24 +155,33 @@ maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char *b
conf->gen.name, box ); conf->gen.name, box );
return 0; 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 ); out = nfmalloc( pl + bl + n + 1 );
memcpy( out, prefix, pl ); memcpy( out, prefix, pl );
p = out + pl; p = out + pl;
if (conf->sub_style == SUB_MAILDIRPP) {
*p++ = '/';
*p++ = '.';
}
while ((c = *box++)) { while ((c = *box++)) {
if (c == '/') { if (c == '/') {
switch (conf->sub_style) { switch (conf->sub_style) {
case SUB_VERBATIM: case SUB_VERBATIM:
*p++ = c; *p++ = c;
break; break;
case SUB_MAILDIRPP:
if (!sub) {
sub = 1;
*p++ = c;
}
*p++ = '.';
break;
case SUB_LEGACY: case SUB_LEGACY:
*p++ = c; *p++ = c;
/* fallthrough */
default: /* SUB_MAILDIRPP */
*p++ = '.'; *p++ = '.';
break; break;
} }
@ -163,17 +193,6 @@ maildir_join_path( maildir_store_conf_t *conf, const char *prefix, const char *b
return out; return out;
} }
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 static int
maildir_validate_path( maildir_store_conf_t *conf ) maildir_validate_path( maildir_store_conf_t *conf )
{ {
@ -212,15 +231,9 @@ maildir_connect_store( store_t *gctx,
cb( DRV_STORE_BAD, aux ); cb( DRV_STORE_BAD, aux );
return; return;
} }
if (conf->gen.trash) { if (conf->gen.trash && !(ctx->trash = maildir_join_path( conf, 0, conf->gen.trash ))) {
if (maildir_ensure_path( conf ) < 0) { cb( DRV_STORE_BAD, aux );
cb( DRV_STORE_BAD, aux ); return;
return;
}
if (!(ctx->trash = maildir_join_path( conf, conf->gen.path, conf->gen.trash ))) {
cb( DRV_STORE_BAD, aux );
return;
}
} }
cb( DRV_OK, aux ); cb( DRV_OK, aux );
} }
@ -325,6 +338,8 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
return -1; return -1;
} }
} else { } else {
char *effName = name;
int nameOff = 0;
if (style == SUB_MAILDIRPP || style == SUB_LEGACY) { if (style == SUB_MAILDIRPP || style == SUB_LEGACY) {
if (*ent == '.') { if (*ent == '.') {
if (!isBox) if (!isBox)
@ -334,15 +349,27 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
if (isBox) if (isBox)
continue; 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 ); 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++) { for (i = nameLen; i < nl; i++) {
if (name[i] == '.') if (name[i] == '.')
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; path[pathLen] = 0;
warn( "Maildir warning: ignoring INBOX in %s\n", path ); warn( "Maildir warning: ignoring INBOX in %s\n", path );
continue; continue;
@ -350,9 +377,9 @@ maildir_list_recurse( store_t *gctx, int isBox, int flags,
path[pl++] = '/'; path[pl++] = '/';
nfsnprintf( path + pl, _POSIX_PATH_MAX - pl, "cur" ); nfsnprintf( path + pl, _POSIX_PATH_MAX - pl, "cur" );
if (!stat( path, &st ) && S_ISDIR(st.st_mode)) if (!stat( path, &st ) && S_ISDIR(st.st_mode))
add_string_list( &gctx->boxes, name ); add_string_list( &gctx->boxes, effName );
if (style == SUB_MAILDIRPP && isBox) { if (style == SUB_MAILDIRPP) {
/* Maildir++ subfolder - don't recurse further. */ /* Maildir++ folder - don't recurse further. */
continue; continue;
} }
path[pl] = 0; 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]; 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( return maildir_list_recurse(
gctx, 1, flags, 0, 0, basePath, basePath ? strlen( basePath ) - 1 : 0, 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 ), 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, maildir_list_store( store_t *gctx, int flags,
void (*cb)( int sts, void *aux ), void *aux ) void (*cb)( int sts, void *aux ), void *aux )
{ {
if ((((flags & LIST_PATH) || ((flags & LIST_PATH_MAYBE) && gctx->conf->path)) maildir_store_conf_t *conf = (maildir_store_conf_t *)gctx->conf;
&& maildir_list_path( gctx, flags, ((maildir_store_conf_t *)gctx->conf)->inbox ) < 0) || if (((((flags & LIST_PATH) && conf->sub_style != SUB_MAILDIRPP)
((flags & LIST_INBOX) && maildir_list_inbox( gctx, flags, gctx->conf->path ) < 0)) { || ((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 ); maildir_invoke_bad_callback( gctx );
cb( DRV_CANCELED, aux ); cb( DRV_CANCELED, aux );
} else { } else {
@ -1111,14 +1142,16 @@ maildir_select_box( store_t *gctx, const char *name )
#endif /* USE_DB */ #endif /* USE_DB */
ctx->fresh[0] = ctx->fresh[1] = 0; ctx->fresh[0] = ctx->fresh[1] = 0;
if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) {
gctx->path = maildir_join_path( conf, conf->inbox, name + 5 ); if (!name[5]) {
ctx->is_inbox = !name[5]; gctx->path = nfstrdup( conf->inbox );
} else { ctx->is_inbox = 1;
if (maildir_ensure_path( conf ) < 0) { } else {
gctx->path = 0; gctx->path = maildir_join_path( conf, 1, name + 5 );
return DRV_CANCELED; 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; ctx->is_inbox = 0;
} }
return gctx->path ? DRV_OK : DRV_BOX_BAD; 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 ); parse_generic_store( &store->gen, cfg );
if (!store->inbox) if (!store->inbox)
store->inbox = expand_strdup( "~/Maildir" ); 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_prefix, "%c2,", store->info_delimiter );
nfasprintf( &store->info_stop, "%c,", store->info_delimiter ); nfasprintf( &store->info_stop, "%c,", store->info_delimiter );
*storep = &store->gen; *storep = &store->gen;

View File

@ -264,17 +264,18 @@ with DOS/Windows file systems.
The on-disk folder naming style used for hierarchical mailboxes. The on-disk folder naming style used for hierarchical mailboxes.
This has option has no effect when \fBFlatten\fR is used. This has option has no effect when \fBFlatten\fR is used.
.br .br
Suppose a mailbox with the canonical path \fItop/sub/subsub\fR, Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and
the styles will yield the following on-disk paths: \fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths:
.br .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) (this is the style you probably want to use)
.br .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 (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 .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) (this is \fBmbsync\fR's historical style)
.br .br
(Default: unset; will error out when sub-folders are encountered) (Default: unset; will error out when sub-folders are encountered)