commit 4f17ae1c3dc738cd4da5c61c2d5e0789cb8315b5
Author: Oswald Buddenhagen <o...@kde.org>
Date:   Sat Aug 11 18:34:46 2012 +0200

    add support for hierarchical mailboxes

 configure.in      |    2 +-
 src/drv_imap.c    |  142 +++++++++++++++++++++++++++++++++------------
 src/drv_maildir.c |  139 ++++++++++++++++++++++++++++++++++----------
 src/isync.h       |   13 ++++-
 src/main.c        |   11 +++-
 src/mbsync.1      |   13 ++++
 src/util.c        |   36 +++++++++++
 7 files changed, 283 insertions(+), 73 deletions(-)

diff --git a/configure.in b/configure.in
index b16eb5d..3e074bf 100644
--- a/configure.in
+++ b/configure.in
@@ -12,7 +12,7 @@ fi
 CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
 
 AC_CHECK_HEADERS(sys/poll.h sys/select.h)
-AC_CHECK_FUNCS(vasprintf)
+AC_CHECK_FUNCS(vasprintf memrchr)
 
 AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
 AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
diff --git a/src/drv_imap.c b/src/drv_imap.c
index 0c15507..692afe5 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -50,6 +50,7 @@ typedef struct imap_store_conf {
        store_conf_t gen;
        imap_server_conf_t *server;
        unsigned use_namespace:1;
+       char delimiter;
 } imap_store_conf_t;
 
 typedef struct imap_message {
@@ -82,6 +83,7 @@ typedef struct imap_store {
        /* trash folder's existence is not confirmed yet */
        enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
        unsigned got_namespace:1;
+       char delimiter; /* hierarchy delimiter */
        list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
        message_t **msgapp; /* FETCH results */
        unsigned caps; /* CAPABILITY results */
@@ -808,21 +810,63 @@ parse_list_rsp( imap_store_t *ctx, char *cmd )
                                return;
                        }
        free_list( list );
-       (void) next_arg( &cmd ); /* skip delimiter */
        arg = next_arg( &cmd );
-       l = strlen( ctx->gen.conf->path );
-       if (memcmp( arg, ctx->gen.conf->path, l ))
-               return;
-       arg += l;
-       if (l && !strcmp( arg, "INBOX" )) {
-               warn( "IMAP warning: ignoring INBOX in %s\n", 
ctx->gen.conf->path );
-               return;
+       if (!ctx->delimiter)
+               ctx->delimiter = *arg;
+       arg = next_arg( &cmd );
+       if (memcmp( arg, "INBOX", 5 ) || (arg[5] && arg[5] != ctx->delimiter)) {
+               l = strlen( ctx->gen.conf->path );
+               if (memcmp( arg, ctx->gen.conf->path, l ))
+                       return;
+               arg += l;
+               if (!memcmp( arg, "INBOX", 5 ) && (!arg[5] || arg[5] == 
ctx->delimiter)) {
+                       if (!arg[5])
+                               warn( "IMAP warning: ignoring INBOX in %s\n", 
ctx->gen.conf->path );
+                       return;
+               }
        }
        if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround 
broken servers */
                return;
+       if (map_name( arg, ctx->delimiter, '/') < 0) {
+               warn( "IMAP warning: ignoring mailbox %s (reserved character 
'/' in name)\n", arg );
+               return;
+       }
        add_string_list( &ctx->gen.boxes, arg );
 }
 
+static int
+prepare_name( char *buf, const imap_store_t *ctx, const char *prefix, const 
char *name )
+{
+       int pl;
+
+       nfsnprintf( buf, 1024, "%s%n%s", prefix, &pl, name );
+       switch (map_name( buf + pl, '/', ctx->delimiter )) {
+       case -1:
+               error( "IMAP error: mailbox name %s contains server's hierarchy 
delimiter\n", buf + pl );
+               return -1;
+       case -2:
+               error( "IMAP error: server's hierarchy delimiter not known\n" );
+               return -1;
+       default:
+               return 0;
+       }
+}
+
+static int
+prepare_box( char *buf, const imap_store_t *ctx )
+{
+       const char *name = ctx->gen.name;
+
+       return prepare_name( buf, ctx,
+           (!memcmp( name, "INBOX", 5 ) && (!name[5] || name[5] == '/')) ? "" 
: ctx->prefix, name );
+}
+
+static int
+prepare_trash( char *buf, const imap_store_t *ctx )
+{
+       return prepare_name( buf, ctx, ctx->prefix, ctx->gen.conf->trash );
+}
+
 struct imap_cmd_trycreate {
        struct imap_cmd gen;
        struct imap_cmd *orig_cmd;
@@ -1157,6 +1201,7 @@ imap_open_store( store_conf_t *conf,
                        ctx->gen.boxes = 0;
                        ctx->gen.listed = 0;
                        ctx->gen.conf = conf;
+                       ctx->delimiter = 0;
                        ctx->callbacks.imap_open = cb;
                        ctx->callback_aux = aux;
                        set_bad_callback( &ctx->gen, (void (*)(void 
*))imap_open_store_bail, ctx );
@@ -1367,10 +1412,9 @@ imap_open_store_namespace( imap_store_t *ctx )
 {
        imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
 
-       ctx->prefix = "";
-       if (*cfg->gen.path)
-               ctx->prefix = cfg->gen.path;
-       else if (cfg->use_namespace && CAP(NAMESPACE)) {
+       ctx->prefix = cfg->gen.path;
+       ctx->delimiter = cfg->delimiter;
+       if (((!*ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && 
CAP(NAMESPACE)) {
                /* get NAMESPACE info */
                if (!ctx->got_namespace)
                        imap_exec( ctx, 0, imap_open_store_namespace_p2, 
"NAMESPACE" );
@@ -1395,11 +1439,20 @@ imap_open_store_namespace_p2( imap_store_t *ctx, struct 
imap_cmd *cmd ATTR_UNUSE
 static void
 imap_open_store_namespace2( imap_store_t *ctx )
 {
-       /* XXX for now assume personal namespace */
-       if (is_list( ctx->ns_personal ) &&
-           is_list( ctx->ns_personal->child ) &&
-           is_atom( ctx->ns_personal->child->child ))
-               ctx->prefix = ctx->ns_personal->child->child->val;
+       imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
+       list_t *nsp, *nsp_1st, *nsp_1st_ns, *nsp_1st_dl;
+
+       /* XXX for now assume 1st personal namespace */
+       if (is_list( (nsp = ctx->ns_personal) ) &&
+           is_list( (nsp_1st = nsp->child) ) &&
+           is_atom( (nsp_1st_ns = nsp_1st->child) ) &&
+           is_atom( (nsp_1st_dl = nsp_1st_ns->next) ))
+       {
+               if (!*ctx->prefix && cfg->use_namespace)
+                       ctx->prefix = nsp_1st_ns->val;
+               if (!ctx->delimiter)
+                       ctx->delimiter = *nsp_1st_dl->val;
+       }
        imap_open_store_finalize( ctx );
 }
 
@@ -1446,15 +1499,14 @@ imap_select( store_t *gctx, int create,
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
        struct imap_cmd_simple *cmd;
-       const char *prefix;
+       char buf[1024];
 
        free_generic_messages( gctx->msgs );
        gctx->msgs = 0;
 
-       if (!strcmp( gctx->name, "INBOX" )) {
-               prefix = "";
-       } else {
-               prefix = ctx->prefix;
+       if (prepare_box( buf, ctx ) < 0) {
+               cb( DRV_BOX_BAD, aux );
+               return;
        }
 
        ctx->gen.uidnext = 0;
@@ -1463,7 +1515,7 @@ imap_select( store_t *gctx, int create,
        cmd->gen.param.create = create;
        cmd->gen.param.trycreate = 1;
        imap_exec( ctx, &cmd->gen, imap_done_simple_box,
-                  "SELECT \"%s%s\"", prefix, gctx->name );
+                  "SELECT \"%s\"", buf );
 }
 
 /******************* imap_load *******************/
@@ -1636,13 +1688,17 @@ imap_trash_msg( store_t *gctx, message_t *msg,
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
        struct imap_cmd_simple *cmd;
+       char buf[1024];
 
        INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
        cmd->gen.param.create = 1;
        cmd->gen.param.to_trash = 1;
+       if (prepare_trash( buf, ctx ) < 0) {
+               cb( DRV_BOX_BAD, aux );
+               return;
+       }
        imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
-                  "UID COPY %d \"%s%s\"",
-                  msg->uid, ctx->prefix, gctx->conf->trash );
+                  "UID COPY %d \"%s\"", msg->uid, buf );
 }
 
 /******************* imap_store_msg *******************/
@@ -1655,9 +1711,8 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int 
to_trash,
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
        struct imap_cmd_out_uid *cmd;
-       const char *prefix, *box;
        int d;
-       char flagstr[128];
+       char flagstr[128], buf[1024];
 
        d = 0;
        if (data->flags) {
@@ -1672,16 +1727,20 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int 
to_trash,
        cmd->out_uid = -2;
 
        if (to_trash) {
-               box = gctx->conf->trash;
-               prefix = ctx->prefix;
                cmd->gen.param.create = 1;
                cmd->gen.param.to_trash = 1;
+               if (prepare_trash( buf, ctx ) < 0) {
+                       cb( DRV_BOX_BAD, -1, aux );
+                       return;
+               }
        } else {
-               box = gctx->name;
-               prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
+               if (prepare_box( buf, ctx ) < 0) {
+                       cb( DRV_BOX_BAD, -1, aux );
+                       return;
+               }
        }
        imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
-                  "APPEND \"%s%s\" %s", prefix, box, flagstr );
+                  "APPEND \"%s\" %s", buf, flagstr );
 }
 
 static void
@@ -1710,15 +1769,20 @@ imap_find_new_msgs( store_t *gctx,
 /******************* imap_list *******************/
 
 static void
-imap_list( store_t *gctx,
+imap_list( store_t *gctx, int flags,
            void (*cb)( int sts, void *aux ), void *aux )
 {
        imap_store_t *ctx = (imap_store_t *)gctx;
-       struct imap_cmd_simple *cmd;
-
-       INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
-       imap_exec( ctx, &cmd->gen, imap_done_simple_box,
-                  "LIST \"\" \"%s%%\"", ctx->prefix );
+       struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, 
aux );
+
+       if (((flags & LIST_PATH) &&
+            imap_exec( ctx, imap_refcounted_new_cmd( sts ), 
imap_refcounted_done_box,
+                       "LIST \"\" \"%s*\"", ctx->prefix ) < 0) ||
+           ((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) &&
+            imap_exec( ctx, imap_refcounted_new_cmd( sts ), 
imap_refcounted_done_box,
+                       "LIST \"\" INBOX*" ) < 0))
+               {}
+       imap_refcounted_done( sts );
 }
 
 /******************* imap_cancel *******************/
@@ -1853,6 +1917,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep, 
int *err )
                                store->use_namespace = parse_bool( cfg );
                        else if (!strcasecmp( "Path", cfg->cmd ))
                                store->gen.path = nfstrdup( cfg->val );
+                       else if (!strcasecmp( "PathDelimiter", cfg->cmd ))
+                               store->delimiter = *cfg->val;
                        else
                                parse_generic_store( &store->gen, cfg, err );
                        continue;
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index d5e740d..74d7b9c 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -94,6 +94,29 @@ maildir_parse_flags( const char *base )
        return flags;
 }
 
+static char *
+maildir_join_path( const char *prefix, const char *box )
+{
+       char *out, *p;
+       int pl, bl, n;
+       char c;
+
+       pl = strlen( prefix );
+       for (bl = 0, n = 0; (c = box[bl]); bl++)
+               if (c == '/')
+                       n++;
+       out = nfmalloc( pl + bl + n + 1 );
+       memcpy( out, prefix, pl );
+       p = out + pl;
+       while ((c = *box++)) {
+               *p++ = c;
+               if (c == '/')
+                       *p++ = '.';
+       }
+       *p = 0;
+       return out;
+}
+
 static void
 maildir_open_store( store_conf_t *conf,
                     void (*cb)( store_t *ctx, void *aux ), void *aux )
@@ -109,7 +132,8 @@ maildir_open_store( store_conf_t *conf,
        ctx = nfcalloc( sizeof(*ctx) );
        ctx->gen.conf = conf;
        ctx->uvfd = -1;
-       nfasprintf( &ctx->trash, "%s%s", conf->path, conf->trash );
+       if (conf->trash)
+               ctx->trash = maildir_join_path( conf->path, conf->trash );
        cb( &ctx->gen, aux );
 }
 
@@ -168,40 +192,87 @@ maildir_invoke_bad_callback( store_t *ctx )
        ctx->bad_callback( ctx->bad_callback_aux );
 }
 
-static void
-maildir_list( store_t *gctx,
-              void (*cb)( int sts, void *aux ), void *aux )
+static int maildir_list_part( store_t *gctx, int doInbox, int *flags );
+
+static int
+maildir_list_recurse( store_t *gctx, int isBox, int *flags, const char *inbox,
+                      char *path, int pathLen, char *name, int nameLen )
 {
        DIR *dir;
+       int pl, nl;
        struct dirent *de;
+       struct stat st;
 
-       if (!(dir = opendir( gctx->conf->path ))) {
-               sys_error( "Maildir error: cannot list %s", gctx->conf->path );
-               maildir_invoke_bad_callback( gctx );
-               cb( DRV_CANCELED, aux );
-               return;
+       if (isBox) {
+               nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "/cur" );
+               if (stat( path, &st ) || !S_ISDIR(st.st_mode))
+                       return 0;
+               path[pathLen] = 0;
+               add_string_list( &gctx->boxes, name );
+               name[nameLen++] = '/';
+       }
+       if (!(dir = opendir( path ))) {
+               sys_error( "Maildir error: cannot list %s", path );
+               return -1;
        }
        while ((de = readdir( dir ))) {
-               const char *inbox = ((maildir_store_conf_t *)gctx->conf)->inbox;
-               int bl, isibx;
-               struct stat st;
-               char buf[PATH_MAX];
-
-               if (*de->d_name == '.')
-                       continue;
-               bl = nfsnprintf( buf, sizeof(buf), "%s%s/cur", 
gctx->conf->path, de->d_name );
-               if (stat( buf, &st ) || !S_ISDIR(st.st_mode))
-                       continue;
-               isibx = !memcmp( buf, inbox, bl - 4 ) && !inbox[bl - 4];
-               if (!isibx && !strcmp( de->d_name, "INBOX" )) {
-                       warn( "Maildir warning: ignoring INBOX in %s\n", 
gctx->conf->path );
-                       continue;
+               const char *ent = de->d_name;
+               pl = pathLen + nfsnprintf( path + pathLen, _POSIX_PATH_MAX - 
pathLen, "%s", ent );
+               if (inbox && !memcmp( path, inbox, pl ) && !inbox[pl]) {
+                       if (maildir_list_part( gctx, 1, flags ) < 0)
+                               return -1;
+               } else {
+                       if (!memcmp( ent, "INBOX", 6 )) {
+                               path[pathLen] = 0;
+                               warn( "Maildir warning: ignoring INBOX in 
%s\n", path );
+                               continue;
+                       }
+                       if (*ent == '.') {
+                               if (!isBox)
+                                       continue;
+                               ent++;
+                       } else {
+                               if (isBox)
+                                       continue;
+                       }
+                       nl = nameLen + nfsnprintf( name + nameLen, 
_POSIX_PATH_MAX - nameLen, "%s", ent );
+                       if (maildir_list_recurse( gctx, 1, flags, inbox, path, 
pl, name, nl ) < 0)
+                               return -1;
                }
-               add_string_list( &gctx->boxes, isibx ? "INBOX" : de->d_name );
        }
        closedir (dir);
+       return 0;
+}
 
-       cb( DRV_OK, aux );
+static int
+maildir_list_part( store_t *gctx, int doInbox, int *flags )
+{
+       int pl, nl;
+       const char *inbox = ((maildir_store_conf_t *)gctx->conf)->inbox;
+       char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
+
+       if (doInbox) {
+               *flags &= ~LIST_INBOX;
+               pl = nfsnprintf( path, _POSIX_PATH_MAX, "%s", inbox );
+               nl = nfsnprintf( name, _POSIX_PATH_MAX, "INBOX" );
+               return maildir_list_recurse( gctx, 1, flags, 0, path, pl, name, 
nl );
+       } else {
+               pl = nfsnprintf( path, _POSIX_PATH_MAX, "%s", gctx->conf->path 
);
+               return maildir_list_recurse( gctx, 0, flags, inbox, path, pl, 
name, 0 );
+       }
+}
+
+static void
+maildir_list( store_t *gctx, int flags,
+              void (*cb)( int sts, void *aux ), void *aux )
+{
+       if (((flags & LIST_PATH) && maildir_list_part( gctx, 0, &flags ) < 0) ||
+           ((flags & LIST_INBOX) && maildir_list_part( gctx, 1, &flags ) < 0)) 
{
+               maildir_invoke_bad_callback( gctx );
+               cb( DRV_CANCELED, aux );
+       } else {
+               cb( DRV_OK, aux );
+       }
 }
 
 static const char *subdirs[] = { "cur", "new", "tmp" };
@@ -237,8 +308,9 @@ maildir_validate( const char *box, int create, 
maildir_store_t *ctx )
 {
        DIR *dirp;
        struct dirent *entry;
+       char *p;
        time_t now;
-       int i, bl;
+       int i, bl, ret;
        struct stat st;
        char buf[_POSIX_PATH_MAX];
 
@@ -246,6 +318,13 @@ maildir_validate( const char *box, int create, 
maildir_store_t *ctx )
        if (stat( buf, &st )) {
                if (errno == ENOENT) {
                        if (create) {
+                               p = memrchr( buf, '/', bl - 1 );
+                               if (*(p + 1) == '.') {
+                                       *p = 0;
+                                       if ((ret = maildir_validate( buf, 1, 
ctx )) != DRV_OK)
+                                               return ret;
+                                       *p = '/';
+                               }
                                if (mkdir( buf, 0700 )) {
                                        sys_error( "Maildir error: cannot 
create mailbox '%s'", buf );
                                        maildir_invoke_bad_callback( &ctx->gen 
);
@@ -822,10 +901,10 @@ maildir_select( store_t *gctx, int create,
 #ifdef USE_DB
        ctx->db = 0;
 #endif /* USE_DB */
-       if (!strcmp( gctx->name, "INBOX" ))
-               gctx->path = nfstrdup( ((maildir_store_conf_t 
*)gctx->conf)->inbox );
-       else
-               nfasprintf( &gctx->path, "%s%s", gctx->conf->path, gctx->name );
+       gctx->path =
+               (!memcmp( gctx->name, "INBOX", 5 ) && (!gctx->name[5] || 
gctx->name[5] == '/')) ?
+                       maildir_join_path( ((maildir_store_conf_t 
*)gctx->conf)->inbox, gctx->name + 5 ) :
+                       maildir_join_path( gctx->conf->path, gctx->name );
 
        if ((ret = maildir_validate( gctx->path, create, ctx )) != DRV_OK) {
                cb( ret, aux );
diff --git a/src/isync.h b/src/isync.h
index 9b23e50..28b3b8e 100644
--- a/src/isync.h
+++ b/src/isync.h
@@ -259,6 +259,9 @@ typedef struct {
 */
 #define DRV_CRLF        1
 
+#define LIST_PATH       1
+#define LIST_INBOX      2
+
 struct driver {
        int flags;
 
@@ -283,8 +286,8 @@ struct driver {
         * Pending commands will have their callbacks synchronously invoked 
with DRV_CANCELED. */
        void (*cancel_store)( store_t *ctx );
 
-       /* List the mailboxes in this store. */
-       void (*list)( store_t *ctx,
+       /* List the mailboxes in this store. Flags are ORed LIST_* values. */
+       void (*list)( store_t *ctx, int flags,
                      void (*cb)( int sts, void *aux ), void *aux );
 
        /* Invoked before select(), this informs the driver which operations 
(OP_*)
@@ -415,6 +418,10 @@ void free_string_list( string_list_t *list );
 
 void free_generic_messages( message_t * );
 
+#ifndef HAVE_MEMRCHR
+void *memrchr( const void *s, int c, size_t n );
+#endif
+
 void *nfmalloc( size_t sz );
 void *nfcalloc( size_t sz );
 void *nfrealloc( void *mem, size_t sz );
@@ -426,6 +433,8 @@ void ATTR_NORETURN oob( void );
 
 char *expand_strdup( const char *s );
 
+int map_name( char *arg, char in, char out );
+
 void sort_ints( int *arr, int len );
 
 void arc4_init( void );
diff --git a/src/main.c b/src/main.c
index a636ab1..d3d0302 100644
--- a/src/main.c
+++ b/src/main.c
@@ -129,7 +129,7 @@ matches( const char *t, const char *p )
                } else if (*p == '%') {
                        p++;
                        do {
-                               if (*t == '.' || *t == '/') /* this is 
"somewhat" hacky ... */
+                               if (*t == '/')
                                        return 0;
                                if (matches( t, p ))
                                        return 1;
@@ -690,6 +690,8 @@ static void
 store_opened( store_t *ctx, void *aux )
 {
        MVARS(aux)
+       string_list_t *cpat;
+       int flags;
 
        if (!ctx) {
                mvars->ret = mvars->skip = 1;
@@ -699,8 +701,13 @@ store_opened( store_t *ctx, void *aux )
        }
        mvars->ctx[t] = ctx;
        if (!mvars->skip && !mvars->boxlist && mvars->chan->patterns && 
!ctx->listed) {
+               for (flags = 0, cpat = mvars->chan->patterns; cpat; cpat = 
cpat->next) {
+                       const char *pat = cpat->string;
+                       if (*pat != '!')
+                               flags |= (!memcmp( pat, "INBOX", 5 ) && 
(!pat[5] || pat[5] == '/')) ? LIST_INBOX : LIST_PATH;
+               }
                set_bad_callback( ctx, store_bad, AUX );
-               mvars->drv[t]->list( ctx, store_listed, AUX );
+               mvars->drv[t]->list( ctx, flags, store_listed, AUX );
        } else {
                mvars->state[t] = ST_OPEN;
                sync_chans( mvars, E_OPEN );
diff --git a/src/mbsync.1 b/src/mbsync.1
index 03178f3..5d41e1a 100644
--- a/src/mbsync.1
+++ b/src/mbsync.1
@@ -105,6 +105,13 @@ There are two auxiliary object classes: Accounts and 
Groups. An Account
 describes the connection part of remote Stores, so a server connection can be
 shared between multiple Stores. A Group aggregates multiple Channels to
 save typing on the command line.
+.P
+File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the
+Store's internal path separators, which may be slashes, periods, etc., or
+even combinations thereof.
+.br
+Mailbox names, OTOH, always use canonical path separators, which are
+Unix-like forward slashes.
 ..
 .SS All Stores
 These options can be used in all supported Store types.
@@ -140,6 +147,7 @@ If \fIsize\fR is 0, the maximum message size is 
\fBunlimited\fR.
 Create a virtual mailbox (relative to \fBPath\fR), which is backed by
 the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
 Channels section.
+This virtual mailbox does not support subfolders.
 ..
 .TP
 \fBTrash\fR \fImailbox\fR
@@ -306,6 +314,11 @@ mailbox names. Disabling this makes sense for some broken 
IMAP servers.
 This option is meaningless if a \fBPath\fR was specified.
 (Default: \fIyes\fR)
 ..
+.TP
+\fBPathDelimiter\fR \fIdelim\fR
+Specify the server's hierarchy delimiter character.
+(Default: taken from the server's first "personal" NAMESPACE)
+..
 .SS Channels
 .TP
 \fBChannel\fR \fIname\fR
diff --git a/src/util.c b/src/util.c
index d3f1638..c6a4a00 100644
--- a/src/util.c
+++ b/src/util.c
@@ -229,6 +229,19 @@ vasprintf( char **strp, const char *fmt, va_list ap )
 }
 #endif
 
+#ifndef HAVE_MEMRCHR
+void *
+memrchr( const void *s, int c, size_t n )
+{
+       u_char *b = (u_char *)s, *e = b + n;
+
+       while (--e >= b)
+               if (*e == c)
+                       return (void *)e;
+       return 0;
+}
+#endif
+
 void
 oob( void )
 {
@@ -378,6 +391,29 @@ expand_strdup( const char *s )
                return nfstrdup( s );
 }
 
+/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no 
out specified */
+int
+map_name( char *arg, char in, char out )
+{
+       int l, k;
+
+       if (!in || in == out)
+               return 0;
+       for (l = 0; arg[l]; l++)
+               if (arg[l] == in) {
+                       if (!out)
+                               return -2;
+                       arg[l] = out;
+               } else if (arg[l] == out) {
+                       /* restore original name for printing error message */
+                       for (k = 0; k < l; k++)
+                               if (arg[k] == out)
+                                       arg[k] = in;
+                       return -1;
+               }
+       return 0;
+}
+
 static int
 compare_ints( const void *l, const void *r )
 {

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to