Hi,Please find attached a patch for isync-1.0.4 which enables synchronising a hierarchy of folders.
The basic idea is to change from using LIST % to LIST *, which I picked up from some post in the mailing list archives. In order to actually get that to work, I had to make some additional changes:
- I am syncing a Dovecot server with a DBMail server and they use different hierarchy delimiters. I have added code to convert folder names on the fly to use the relevant server's convention (determined automatically); the code always uses '.' locally so this can be used in the config file regardless.
- I found a problem when synchronising a new folder, which turned out to be because UIDNEXT returning 0 was regarded as an error. I have modified the code to distinguish betwen atoi() returning 0 because the ID is zero and it returning 0 because it couldn't convert the ID.
I don't know how acceptable this patch will be. However, I have been using it without problems for the last three months and I thought it might be useful to someone else even if it can't be merged.
Cheers. Steve
diff --unified --recursive isync-1.0.4/src/drv_imap.c isync-1.0.4-sf/src/drv_imap.c --- isync-1.0.4/src/drv_imap.c 2007-09-22 09:44:12.000000000 +0100 +++ isync-1.0.4-sf/src/drv_imap.c 2010-06-22 03:10:22.000000000 +0100 @@ -80,6 +80,8 @@ /* int seq; will be needed when expunges are tracked */ } imap_message_t; +#define CANONICAL_HIERARCHY_DELIMITER '.' + #define NIL (void*)0x1 #define LIST (void*)0x2 @@ -126,6 +128,7 @@ store_t gen; imap_t *imap; const char *prefix; + char hierarchy_delimiter; unsigned /*currentnc:1,*/ trashnc:1; } imap_store_t; @@ -559,6 +562,24 @@ } */ +static void +canonicalise_name( imap_store_t *ctx, char *name ) +{ + for ( ; *name; ++name) + if (*name == ctx->hierarchy_delimiter) + *name = CANONICAL_HIERARCHY_DELIMITER; +} + +static char * +non_canonical_name( imap_store_t *ctx, const char *name ) +{ + char *s = nfstrdup( name ), *p = s; + for ( ; *p; p++) + if (*p == CANONICAL_HIERARCHY_DELIMITER) + *p = ctx->hierarchy_delimiter; + return s; +} + static int is_atom( list_t *list ) { @@ -834,7 +855,10 @@ return RESP_BAD; } } else if (!strcmp( "UIDNEXT", arg )) { - if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) { + /* atoi() indicates failure by returning 0, but it is OK for next UID to be 0 (this can happen + * with a newly created mailbox) + */ + if (!(arg = next_arg( &s )) || (!(imap->uidnext = atoi( arg )) && strcmp( arg, "0" ))) { fprintf( stderr, "IMAP error: malformed NEXTUID status\n" ); return RESP_BAD; } @@ -900,7 +924,13 @@ return; } free_list( list ); - (void) next_arg( &cmd ); /* skip delimiter */ + + /* Most of the time the delimiter is known but when we first log on to the server we force a LIST + * operation to ensure this gets populated before it's used. TODO: It would be cleaner just to parse + * the response to that LIST using special-purpose code at that point. */ + arg = next_arg( &cmd ); + ctx->hierarchy_delimiter = (arg[0] ? arg[0] : CANONICAL_HIERARCHY_DELIMITER); + arg = next_arg( &cmd ); l = strlen( ctx->gen.conf->path ); if (memcmp( arg, ctx->gen.conf->path, l )) @@ -908,6 +938,9 @@ arg += l; if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround broken servers */ return; + + canonicalise_name( ctx, arg ); + add_string_list( &imap->boxes, arg ); } @@ -1185,6 +1218,20 @@ } #endif +static int +imap_list( store_t *gctx, string_list_t **retb ) +{ + imap_store_t *ctx = (imap_store_t *)gctx; + imap_t *imap = ctx->imap; + int ret; + + imap->boxes = 0; + if ((ret = imap_exec_b( ctx, 0, "LIST \"\" \"%s*\"", ctx->prefix )) != DRV_OK) + return ret; + *retb = imap->boxes; + return DRV_OK; +} + static store_t * imap_open_store( store_conf_t *conf, store_t *oldctx ) { @@ -1196,6 +1243,7 @@ struct hostent *he; struct sockaddr_in addr; int s, a[2], preauth; + string_list_t *dummysl = 0; #if HAVE_LIBSSL int use_ssl; #endif @@ -1394,6 +1442,15 @@ ctx->prefix = imap->ns_personal->child->child->val; } ctx->trashnc = 1; + + /* Force a LIST so we know the hierarchy delimiter. TODO: This is a bit wasteful; a LIST "" "" would be + * good enough. TODO: We piggyback on the general response parsing code in parse_list_rsp(), which is + * a bit ugly. */ + if (imap_list( &(ctx->gen), &dummysl ) != DRV_OK) + goto bail; + free_string_list( dummysl ); + info( "Hierarchy delimiter is '%c'\n", ctx->hierarchy_delimiter ); + return (store_t *)ctx; bail: @@ -1415,6 +1472,7 @@ imap_store_t *ctx = (imap_store_t *)gctx; imap_t *imap = ctx->imap; const char *prefix; + char *ncname = 0; int ret, i, j, bl; struct imap_cmd_cb cb; char buf[1000]; @@ -1431,7 +1489,8 @@ memset( &cb, 0, sizeof(cb) ); cb.create = (gctx->opts & OPEN_CREATE) != 0; cb.trycreate = 1; - if ((ret = imap_exec_b( ctx, &cb, "SELECT \"%s%s\"", prefix, gctx->name )) != DRV_OK) + ncname = non_canonical_name( ctx, gctx->name ); + if ((ret = imap_exec_b( ctx, &cb, "SELECT \"%s%s\"", prefix, ncname )) != DRV_OK) goto bail; if (gctx->count) { @@ -1466,6 +1525,8 @@ bail: if (excs) free( excs ); + if (ncname) + free( ncname ); return ret; } @@ -1553,7 +1614,7 @@ imap_store_t *ctx = (imap_store_t *)gctx; imap_t *imap = ctx->imap; struct imap_cmd_cb cb; - char *fmap, *buf; + char *fmap, *buf, *ncbox; const char *prefix, *box; int ret, i, j, d, len, extra, nocr; int start, sbreak = 0, ebreak = 0; @@ -1650,7 +1711,9 @@ imap->caps = imap->rcaps & ~(1 << LITERALPLUS);*/ } cb.ctx = uid; - ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr ); + ncbox = non_canonical_name( ctx, box ); + ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, ncbox, flagstr ); + free( ncbox ); imap->caps = imap->rcaps; if (ret != DRV_OK) return ret; @@ -1671,20 +1734,6 @@ } static int -imap_list( store_t *gctx, string_list_t **retb ) -{ - imap_store_t *ctx = (imap_store_t *)gctx; - imap_t *imap = ctx->imap; - int ret; - - imap->boxes = 0; - if ((ret = imap_exec_b( ctx, 0, "LIST \"\" \"%s%%\"", ctx->prefix )) != DRV_OK) - return ret; - *retb = imap->boxes; - return DRV_OK; -} - -static int imap_check( store_t *gctx ) { (void) gctx; diff --unified --recursive isync-1.0.4/src/main.c isync-1.0.4-sf/src/main.c --- isync-1.0.4/src/main.c 2008-02-23 09:12:51.000000000 +0000 +++ isync-1.0.4-sf/src/main.c 2010-06-22 02:44:02.000000000 +0100 @@ -95,6 +95,8 @@ } else if (*p == '%') { p++; do { + /* TODO: It may or may not be a good idea to use the IMAP server reported + * hierarchy delimiter here. */ if (*t == '.' || *t == '/') /* this is "somewhat" hacky ... */ return 0; if (matches( t, p ))
------------------------------------------------------------------------------ Start uncovering the many advantages of virtual appliances and start using them to simplify application deployment and accelerate your shift to cloud computing. http://p.sf.net/sfu/novell-sfdev2dev
_______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel