isync

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

commit 46584e5358dbd32b8549b15ce543c33482bc6d60
parent 3bfc3c5063b5c5a02d77c97dd43461e7e19ca0f7
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Fri,  3 Jun 2022 20:58:09 +0200

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.

Diffstat:
MNEWS | 2++
Msrc/drv_maildir.c | 38++++++++++++++++++++++++++++++++------
Msrc/mbsyncrc.sample | 26++++++++++++++++++++++++++
3 files changed, 60 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS @@ -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 Maildir Paths with suffixes. + The unfiltered list of mailboxes in each Store can be printed now. A proper summary is now printed prior to exiting. diff --git a/src/drv_maildir.c b/src/drv_maildir.c @@ -35,6 +35,7 @@ typedef union maildir_store_conf { struct { STORE_CONF char *path; + char *path_sfx; char *inbox; #ifdef USE_DB int alt_map; @@ -121,16 +122,20 @@ static char * maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box ) { char *out, *p; - const char *prefix; - uint pl, bl, n; + const char *prefix, *infix; + uint pl, il, bl, n; char c; if (in_inbox || conf->sub_style == SUB_MAILDIRPP) { prefix = conf->inbox; + infix = NULL; + il = 0; } else { if (maildir_ensure_path( conf ) < 0) return NULL; prefix = conf->path; + infix = conf->path_sfx; + il = strlen( infix ) + 1; } pl = strlen( prefix ); 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 */ break; } - out = nfmalloc( pl + bl + n + 1 ); + out = nfmalloc( pl + il + bl + n + 1 ); memcpy( out, prefix, pl ); p = out + pl; if (conf->sub_style == SUB_MAILDIRPP) { *p++ = '/'; *p++ = '.'; + } else if (il) { + *p++ = '/'; + memcpy( p, infix, il - 1 ); + p += il - 1; } while ((c = *box++)) { if (c == '/') { @@ -366,6 +375,7 @@ maildir_list_maildirpp( maildir_store_t *ctx, int flags, const char *inbox ) static int maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags, const char *inbox, uint inboxLen, + char *suffix, int suffixLen, char *path, int pathLen, char *name, int nameLen ) { 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 ); if (pl == 3 && (!memcmp( ent, "cur", 3 ) || !memcmp( ent, "new", 3 ) || !memcmp( ent, "tmp", 3 ))) 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; if (inbox && equals( path, pl, inbox, inboxLen )) { // 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 ); path[pl] = 0; 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 ); return -1; } @@ -450,7 +470,7 @@ maildir_list_inbox( maildir_store_t *ctx, int flags ) add_string_list( &ctx->boxes, "INBOX" ); 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 ), 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; return maildir_list_recurse( 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 ); } @@ -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] == '/') { error( "Maildir store '%s': Path cannot be nested under Inbox\n", store->name ); cfg->err = 1; + } else { + char *s = strrchr( store->path, '/' ); + assert( s ); // due to expand_strdup() + store->path_sfx = s + 1; + *s = 0; } } } diff --git a/src/mbsyncrc.sample b/src/mbsyncrc.sample @@ -57,6 +57,32 @@ Group boxes 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 Host st1.domain.com AuthMech CRAM-MD5