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
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel