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

Reply via email to