add support for Maildir Paths with suffixes

that is, Path not ending with a slash.

pedantically, this is a bugfix, as the manual already suggested that
this is possible (and at least one user got the hint, though he was
disappointed).

the IMAP driver already supports this.
This commit is contained in:
Oswald Buddenhagen 2022-06-03 20:58:09 +02:00
parent 3bfc3c5063
commit 46584e5358
3 changed files with 60 additions and 6 deletions

2
NEWS
View File

@ -21,6 +21,8 @@ MaxMessages and MaxSize can be used together now.
Added support for IMAP mailbox names with non-ASCII characters. Added support for IMAP mailbox names with non-ASCII characters.
Added support for Maildir Paths with suffixes.
The unfiltered list of mailboxes in each Store can be printed now. The unfiltered list of mailboxes in each Store can be printed now.
A proper summary is now printed prior to exiting. A proper summary is now printed prior to exiting.

View File

@ -35,6 +35,7 @@ typedef union maildir_store_conf {
struct { struct {
STORE_CONF STORE_CONF
char *path; char *path;
char *path_sfx;
char *inbox; char *inbox;
#ifdef USE_DB #ifdef USE_DB
int alt_map; int alt_map;
@ -121,16 +122,20 @@ static char *
maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box ) maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box )
{ {
char *out, *p; char *out, *p;
const char *prefix; const char *prefix, *infix;
uint pl, bl, n; uint pl, il, bl, n;
char c; char c;
if (in_inbox || conf->sub_style == SUB_MAILDIRPP) { if (in_inbox || conf->sub_style == SUB_MAILDIRPP) {
prefix = conf->inbox; prefix = conf->inbox;
infix = NULL;
il = 0;
} else { } else {
if (maildir_ensure_path( conf ) < 0) if (maildir_ensure_path( conf ) < 0)
return NULL; return NULL;
prefix = conf->path; prefix = conf->path;
infix = conf->path_sfx;
il = strlen( infix ) + 1;
} }
pl = strlen( prefix ); pl = strlen( prefix );
for (bl = 0, n = 0; (c = box[bl]); bl++) { for (bl = 0, n = 0; (c = box[bl]); bl++) {
@ -157,12 +162,16 @@ maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box )
default: /* SUB_LEGACY and SUB_UNSET */ default: /* SUB_LEGACY and SUB_UNSET */
break; break;
} }
out = nfmalloc( pl + bl + n + 1 ); out = nfmalloc( pl + il + bl + n + 1 );
memcpy( out, prefix, pl ); memcpy( out, prefix, pl );
p = out + pl; p = out + pl;
if (conf->sub_style == SUB_MAILDIRPP) { if (conf->sub_style == SUB_MAILDIRPP) {
*p++ = '/'; *p++ = '/';
*p++ = '.'; *p++ = '.';
} else if (il) {
*p++ = '/';
memcpy( p, infix, il - 1 );
p += il - 1;
} }
while ((c = *box++)) { while ((c = *box++)) {
if (c == '/') { if (c == '/') {
@ -366,6 +375,7 @@ maildir_list_maildirpp( maildir_store_t *ctx, int flags, const char *inbox )
static int static int
maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags, maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags,
const char *inbox, uint inboxLen, const char *inbox, uint inboxLen,
char *suffix, int suffixLen,
char *path, int pathLen, char *name, int nameLen ) char *path, int pathLen, char *name, int nameLen )
{ {
DIR *dir; DIR *dir;
@ -399,6 +409,16 @@ maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags,
pl = nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s", ent ); 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 ))) if (pl == 3 && (!memcmp( ent, "cur", 3 ) || !memcmp( ent, "new", 3 ) || !memcmp( ent, "tmp", 3 )))
continue; continue;
if (suffixLen) {
if (!starts_with( ent, pl, suffix, suffixLen ))
continue;
if (pl == suffixLen) {
error( "Maildir error: empty mailbox name under %s - did you forget the trailing slash?\n", path );
closedir( dir );
return -1;
}
ent += suffixLen;
}
pl += pathLen; pl += pathLen;
if (inbox && equals( path, pl, inbox, inboxLen )) { if (inbox && equals( path, pl, inbox, inboxLen )) {
// Inbox nested into Path. // Inbox nested into Path.
@ -429,7 +449,7 @@ maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags,
add_string_list( &ctx->boxes, name ); add_string_list( &ctx->boxes, name );
path[pl] = 0; path[pl] = 0;
name[nl++] = '/'; name[nl++] = '/';
if (maildir_list_recurse( ctx, isBox + 1, flags, inbox, inboxLen, path, pl, name, nl ) < 0) { if (maildir_list_recurse( ctx, isBox + 1, flags, inbox, inboxLen, NULL, 0, path, pl, name, nl ) < 0) {
closedir( dir ); closedir( dir );
return -1; return -1;
} }
@ -450,7 +470,7 @@ maildir_list_inbox( maildir_store_t *ctx, int flags )
add_string_list( &ctx->boxes, "INBOX" ); add_string_list( &ctx->boxes, "INBOX" );
return maildir_list_recurse( return maildir_list_recurse(
ctx, 1, flags, NULL, 0, ctx, 1, flags, NULL, 0, NULL, 0,
path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->inbox ), path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->inbox ),
name, nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/" ) ); name, nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/" ) );
} }
@ -469,7 +489,8 @@ maildir_list_path( maildir_store_t *ctx, int flags )
const char *inbox = ctx->conf->inbox; const char *inbox = ctx->conf->inbox;
return maildir_list_recurse( return maildir_list_recurse(
ctx, 0, flags, inbox, strlen( inbox ), ctx, 0, flags, inbox, strlen( inbox ),
path, nfsnprintf( path, _POSIX_PATH_MAX, "%s", ctx->conf->path ), ctx->conf->path_sfx, strlen( ctx->conf->path_sfx ),
path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->path ),
name, 0 ); name, 0 );
} }
@ -1914,6 +1935,11 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
if (starts_with( store->path, -1, store->inbox, inboxLen ) && store->path[inboxLen] == '/') { if (starts_with( store->path, -1, store->inbox, inboxLen ) && store->path[inboxLen] == '/') {
error( "Maildir store '%s': Path cannot be nested under Inbox\n", store->name ); error( "Maildir store '%s': Path cannot be nested under Inbox\n", store->name );
cfg->err = 1; cfg->err = 1;
} else {
char *s = strrchr( store->path, '/' );
assert( s ); // due to expand_strdup()
store->path_sfx = s + 1;
*s = 0;
} }
} }
} }

View File

@ -57,6 +57,32 @@ Group boxes
Channels work personal remote Channels work personal remote
# Due to the divergent Path suffixes, it's possible to have
# multiple Stores homed in the same directory.
# You could even put them all directly into $HOME.
MaildirStore local-personal
Path ~/Mail/personal-
Inbox ~/Mail/personal-INBOX
MaildirStore local-work
Path ~/Mail/work-
# Just because.
Inbox ~/Mail/w0rk_InBoX
Channel personal-joined
Far :personal:
Near :local-personal:
Paterns *
Channel work-joined
Far :work:
Near :local-work:
Paterns *
Group joined personal-joined work-joined
IMAPStore st1 IMAPStore st1
Host st1.domain.com Host st1.domain.com
AuthMech CRAM-MD5 AuthMech CRAM-MD5