QRESYNC implementation for Camel-lite



Hi there fellow hackers and other insane people, like me, who spend all
night coding on proposed IMAP protocol enhancements.

I implemented support for QRESYNC in Tinymail's camel-lite. I never
tested this because somehow the MBox copy that the friendly guys at
Isode gave me, is not starting up and I know of no other IMAP server
that already offers the QRESYNC capability.

I'll retry tomorrow after actually reading the documentation that came
with it.

Therefore, is this implementation based on what I've found on Draft #6
of the "IMAP4 Extensions for Quick Mailbox Resynchronization" which is
available here:

http://www3.tools.ietf.org/html/draft-ietf-lemonade-reconnect-client-06

Especially for Dave and Alexey: you'd be my heroes if you could take a
look at the things I'm changing in the patch. Especially those things
that are related to the protocol.

I'm for example not sure about the VANISHED reply (and the exact meaning
of that "earlier" parameter).

ps. I added the nice folks at Evolution in CC, as some of these code
warriors are planning to migrate some of newer features in camel-lite to
upstream camel.

-- 
Philip Van Hoof, software developer
home: me at pvanhoof dot be 
gnome: pvanhoof at gnome dot org 
http://www.pvanhoof.be/blog



Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c	(revision 2841)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c	(working copy)
@@ -702,6 +702,8 @@
 	{ "CONDSTORE",          IMAP_CAPABILITY_CONDSTORE },
 	{ "IDLE",         	IMAP_CAPABILITY_IDLE },	
 	{ "BINARY",         	IMAP_CAPABILITY_BINARY },
+	{ "QRESYNC",         	IMAP_CAPABILITY_QRESYNC },
+
 	{ NULL, 0 }
 };
 
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h	(revision 2841)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h	(working copy)
@@ -119,6 +119,7 @@
 #define IMAP_CAPABILITY_CONDSTORE		(1 << 12)
 #define IMAP_CAPABILITY_IDLE			(1 << 13)
 #define IMAP_CAPABILITY_BINARY			(1 << 14)
+#define IMAP_CAPABILITY_QRESYNC			(1 << 15)
 
 #define IMAP_PARAM_OVERRIDE_NAMESPACE		(1 << 0)
 #define IMAP_PARAM_CHECK_ALL			(1 << 1)
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c	(revision 2841)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c	(working copy)
@@ -120,6 +120,9 @@
 static void imap_rename (CamelFolder *folder, const char *new);
 static void imap_set_push_email (CamelFolder *folder, gboolean setting);
 
+static int process_condstore_line (CamelImapFolder *imap_folder, char *resp, CamelFolderChangeInfo *changes);
+
+
 /* message manipulation */
 static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid,
 					   CamelFolderReceiveType type, gint param, CamelException *ex);
@@ -151,9 +154,10 @@
 static GPtrArray *imap_search_by_uids	    (CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex);
 static void       imap_search_free          (CamelFolder *folder, GPtrArray *uids);
 static int imap_get_local_size (CamelFolder *folder);
-
 static void imap_thaw (CamelFolder *folder);
 
+static void camel_imap_folder_changed_for_uids (CamelFolder *folder, GPtrArray *expunged, CamelFolderChangeInfo *changes);
+
 static CamelObjectClass *parent_class;
 
 static GData *parse_fetch_response (CamelImapFolder *imap_folder, char *msg_att);
@@ -406,6 +410,12 @@
 	return retval;
 }
 
+char*
+camel_imap_folder_get_highestmodseq (CamelImapFolder *imap_folder)
+{
+	return get_highestmodseq (imap_folder);
+}
+
 /* Called with the store's connect_lock locked */
 
 
@@ -444,6 +454,7 @@
 	char *resp, *phighestmodseq = NULL, *highestmodseq = NULL;
 	CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
 	gboolean removals = FALSE, condstore = FALSE, needtoput=FALSE, suc=FALSE;
+	CamelFolderChangeInfo *changes = NULL;
 
 	count = camel_folder_summary_count (folder->summary);
 
@@ -522,7 +533,38 @@
 				 * else tries to interpret it. */
 				g_free (response->untagged->pdata[i]);
 				g_ptr_array_remove_index (response->untagged, i--);
+			} else if (!g_ascii_strncasecmp (resp, " FETCH", 6)) {
+				/* QRESYNC! Sanity! 
+				 * Making the copy sucks a little bit, but I'd
+				 * having to rewrite the parsing thing of this
+				 * Camel IMAP crap :-) */
+				char *nresp = g_strdup_printf ("* %s", resp);
+				if (changes == NULL)
+					changes = camel_folder_change_info_new();
+				if (process_condstore_line (imap_folder, nresp, changes) == -1)
+					g_warning ("Invalid QRESYNC response: (%s)", nresp);
+				g_free (nresp);
 			}
+
+		} else if (!g_ascii_strncasecmp (resp, "VANISHED", 8)) 
+		{
+			/* QRESYNC! Sanity! */
+			char *str = strchr (resp+8, ')');
+			char *uidset, *lasts = NULL;
+
+			if (str)
+				str++;
+			else 
+				str = resp+9;
+
+			for (uidset = strtok_r (str, ",", &lasts); uidset; uidset = strtok_r (NULL, ",", &lasts)) {
+				GPtrArray *parray = imap_uid_set_to_array (folder->summary, uidset);
+				if (changes == NULL)
+					changes = camel_folder_change_info_new();
+				camel_imap_folder_changed_for_uids (folder, parray, changes);
+				imap_uid_array_free (parray);
+			}
+
 		}
 	}
 
@@ -552,7 +594,9 @@
 	/* If there's no match on VALIDITY, we are dealing with a different 
 	 * folder. For example the folder got removed and re-created. We'll
 	 * simply clear everyting and do things from scratch. */
-	
+
+	/* TODO: With QRESYNC we can also let the IMAP server do this check */
+
 	if (!imap_summary->validity)
 		imap_summary->validity = validity;
 	else if (validity != imap_summary->validity) {
@@ -588,103 +632,116 @@
 	 * happened. This CONDSTORE code does not yet support expunges. 
 	 * Therefore we will simply use the old code. */
 
-	if (phighestmodseq != NULL && (val != uidnext-1))
+	if (!(store->capabilities & IMAP_CAPABILITY_QRESYNC))
 	{
-		g_free (phighestmodseq); 
-		phighestmodseq = NULL;
-		removals = TRUE;
-	}
-
-	/* If our local count isn't the same as the EXISTS, then our CONDSTORE 
-	 * implementation can't be used. Period. */
-	
-	if (exists != count)
-	{
-		if (phighestmodseq != NULL)
+		if (phighestmodseq != NULL && (val != uidnext-1))
+		{
 			g_free (phighestmodseq); 
-		phighestmodseq = NULL;
-		removals = TRUE;
-	}
+			phighestmodseq = NULL;
+			removals = TRUE;
+		}
 
-	if (removals)
-		imap_folder->need_rescan = TRUE;
-	else if (condstore && (store->capabilities & IMAP_CAPABILITY_CONDSTORE))
-		imap_folder->need_rescan = FALSE;
+		/* If our local count isn't the same as the EXISTS, then our CONDSTORE 
+		 * implementation can't be used. Period. */
 
-	/* We still aren't certain. For example if at the end of the mailbox 
-	 * both an add and an expunge happened, then all of above figured out
-	 * nothing meaningful. So we will compare the last local uid with the
-	 * remote uid at the same sequence number (that's count, as the count
-	 * is the index + 1). This is where we need the VAL thingy of above. */
-	
-	if (!imap_folder->need_rescan) 
-	{
-		int mval = 0;
+		if (exists != count)
+		{
+			if (phighestmodseq != NULL)
+				g_free (phighestmodseq); 
+			phighestmodseq = NULL;
+			removals = TRUE;
+		}
 
-		/* If the UID of the highest message we know about has changed, 
-		 * then that indicates that messages have been both added and 
-		 * removed, so we have to rescan to find the removed ones */
+		if (removals)
+			imap_folder->need_rescan = TRUE;
+		else if (condstore && (store->capabilities & IMAP_CAPABILITY_CONDSTORE))
+			imap_folder->need_rescan = FALSE;
 
-		if (count != 0)
+		/* We still aren't certain. For example if at the end of the mailbox 
+		 * both an add and an expunge happened, then all of above figured out
+		 * nothing meaningful. So we will compare the last local uid with the
+		 * remote uid at the same sequence number (that's count, as the count
+		 * is the index + 1). This is where we need the VAL thingy of above. */
+		
+		if (!imap_folder->need_rescan) 
 		{
-			response = camel_imap_command (store, NULL, ex, "FETCH %d UID", count);
-			if (response) {
-				uid = 0;
-				for (i = 0; i < response->untagged->len; i++) 
-				{
-					resp = response->untagged->pdata[i];
-					mval = strtoul (resp + 2, &resp, 10);
-					if (mval == 0)
-						continue;
-					if (!g_ascii_strcasecmp (resp, " EXISTS")) 
+			int mval = 0;
+
+			/* If the UID of the highest message we know about has changed, 
+			 * then that indicates that messages have been both added and 
+			 * removed, so we have to rescan to find the removed ones */
+
+			if (count != 0)
+			{
+				response = camel_imap_command (store, NULL, ex, "FETCH %d UID", count);
+				if (response) {
+					uid = 0;
+					for (i = 0; i < response->untagged->len; i++) 
 					{
-						/* Another one?? */
-						exists = mval;
-						continue;
+						resp = response->untagged->pdata[i];
+						mval = strtoul (resp + 2, &resp, 10);
+						if (mval == 0)
+							continue;
+						if (!g_ascii_strcasecmp (resp, " EXISTS")) 
+						{
+							/* Another one?? */
+							exists = mval;
+							continue;
+						}
+						if (uid != 0 || mval != count || g_ascii_strncasecmp (resp, " FETCH (", 8) != 0)
+							continue;
+						
+						fetch_data = parse_fetch_response (imap_folder, resp + 7);
+						uid = strtoul (g_datalist_get_data (&fetch_data, "UID"), NULL, 10);
+						g_datalist_clear (&fetch_data);
 					}
-					if (uid != 0 || mval != count || g_ascii_strncasecmp (resp, " FETCH (", 8) != 0)
-						continue;
-					
-					fetch_data = parse_fetch_response (imap_folder, resp + 7);
-					uid = strtoul (g_datalist_get_data (&fetch_data, "UID"), NULL, 10);
-					g_datalist_clear (&fetch_data);
-				}
-				camel_imap_response_free_without_processing (store, response);
-				if (uid == 0 || uid != val)
+					camel_imap_response_free_without_processing (store, response);
+					if (uid == 0 || uid != val)
+						imap_folder->need_rescan = TRUE;
+				} else 
 					imap_folder->need_rescan = TRUE;
-			} else 
+			} else
 				imap_folder->need_rescan = TRUE;
-		} else
-			imap_folder->need_rescan = TRUE;
-	}
+		}
 
-	/* Okay, it survived ALL checks and CONDSTORE is available too. Lucky we 
-	 * are, aren't we? So we use CONDSTORE. Note, however, that if condstore 
-	 * still finds removals (sequences don't match) or if anything goes wrong
-	 * during the CONDSTORE code, that it'll set need_rescan TRUE and that 
-	 * as a result the lines below this if{} block will still happen. This is
-	 * indeed on purpose and as a fall-back situation (our detection got it
-	 * wrong) */
-	
-	if (phighestmodseq)
-	{
-		if (!imap_folder->need_rescan)
-			suc = imap_rescan_condstore (folder, exists, phighestmodseq, ex);
-		g_free (phighestmodseq);
-		phighestmodseq = NULL;
+
+		/* Okay, it survived ALL checks and CONDSTORE is available too. Lucky we 
+		 * are, aren't we? So we use CONDSTORE. Note, however, that if condstore 
+		 * still finds removals (sequences don't match) or if anything goes wrong
+		 * during the CONDSTORE code, that it'll set need_rescan TRUE and that 
+		 * as a result the lines below this if{} block will still happen. This is
+		 * indeed on purpose and as a fall-back situation (our detection got it
+		 * wrong) */
+		
+		if (phighestmodseq)
+		{
+			if (!imap_folder->need_rescan)
+				suc = imap_rescan_condstore (folder, exists, phighestmodseq, ex);
+			g_free (phighestmodseq);
+			phighestmodseq = NULL;
+		}
+
+		if (imap_folder->need_rescan)
+			suc = imap_rescan (folder, exists, ex);
+		else if (exists > count)
+			camel_imap_folder_changed (folder, exists, NULL, ex);
+	} else {
+		/* Wow, this IMAP server rocks! it has QRESYNC! Hi there Isode!*/
+		suc = TRUE;
+		needtoput = TRUE;
 	}
 
-	if (imap_folder->need_rescan)
-		suc = imap_rescan (folder, exists, ex);
-	else if (exists > count)
-		camel_imap_folder_changed (folder, exists, NULL, ex);
-
 	if (highestmodseq != NULL && suc && needtoput)
 		put_highestmodseq (imap_folder, (const char *) highestmodseq);
 
 	if (highestmodseq != NULL)
 		g_free (highestmodseq);
 
+	if (changes) {
+		camel_object_trigger_event(CAMEL_OBJECT (folder), "folder_changed", changes);
+		camel_folder_change_info_free(changes);
+	}
+
 	if (idle)
 		camel_imap_folder_start_idle (folder);
 }
@@ -906,6 +963,89 @@
 
 #endif
 
+
+static int
+process_condstore_line (CamelImapFolder *imap_folder, char *resp, CamelFolderChangeInfo *changes) 
+{
+	gint retval = 0, summary_len;
+	GData *data;
+	char *uid;
+	guint32 flags, seq;
+	CamelMessageInfo *info;
+	CamelImapMessageInfo *iinfo;
+	CamelFolder *folder = (CamelFolder *) imap_folder;
+
+	summary_len = camel_folder_summary_count (folder->summary);
+	data = parse_fetch_response (imap_folder, resp);
+
+	if (!data)
+		return -1;
+
+	uid = g_datalist_get_data (&data, "UID");
+	flags = GPOINTER_TO_UINT (g_datalist_get_data (&data, "FLAGS"));
+	seq = GPOINTER_TO_UINT (g_datalist_get_data (&data, "SEQUENCE"));
+
+	/* So basically: if the UID is not found locally, we have flags
+	 * for a new message. That's cool, but not an error as the 
+	 * camel_imap_folder_changed will launch imap_update_summary 
+	 * who will deal with that.
+	 * 
+	 * See, we wont find it if it's larger than the count of the
+	 * local summary list. We don't even have to check that, right? 
+	 * 
+	 * We also check for negative sequences. No reason for that, 
+	 * it's just wrong (actually impossible since it's a unsigned 
+	 * int, but it's just for security-clarity here. Let it be, it 
+	 * doesn't hurt either) */
+	
+	if (!uid /*|| seq < 0 */|| seq-1 > summary_len) {
+		g_datalist_clear (&data);
+		return -1;
+	}
+
+	info = camel_folder_summary_index (folder->summary, seq-1);
+	iinfo = (CamelImapMessageInfo *) info;
+
+	/* However, if we do find the message-info at that index then we 
+	 * we should also check whether the UID matches. If not, well ..
+	 * that's not good eh. That means that we shouldn't have been 
+	 * using this CONDSTORE code in the first place! Bad luck, we 
+	 * just set need_rescan TRUE and let the selected metod launch
+	 * the conventional imap_rescan later-on. Since we are receiving 
+	 * stuff anyway, and we need to read that away nonetheless, we
+	 * just continue doing our stuff. */
+	
+	if (info) 
+	{
+		/* printf ("%s vs %s on %d\n", info->uid, uid, seq-1); */
+		if (!strcmp (info->uid, uid))
+		{
+		  if (flags != iinfo->server_flags) 
+		  {
+			guint32 server_set, server_cleared;
+
+			camel_folder_summary_touch (folder->summary);
+			server_set = flags & ~iinfo->server_flags;
+			server_cleared = iinfo->server_flags & ~flags;
+			iinfo->info.flags = (iinfo->info.flags | server_set) & ~server_cleared;
+			iinfo->server_flags = flags;
+			if (changes)
+				camel_folder_change_info_change_uid(changes, uid);
+			/* flags_to_label(folder, (CamelImapMessageInfo *)info); */
+		  }
+		  camel_message_info_free (info);
+		} else {
+			imap_folder->need_rescan = TRUE;
+			retval = FALSE;
+		}
+	} else 
+		retval = -1;
+
+	g_datalist_clear (&data);
+
+	return retval;
+}
+
 static gboolean 
 imap_rescan_condstore (CamelFolder *folder, int exists, const char *highestmodseq, CamelException *ex)
 {
@@ -922,8 +1062,6 @@
 	char *resp;
 	CamelImapResponseType type;
 	int summary_len, summary_got;
-	CamelMessageInfo *info;
-	CamelImapMessageInfo *iinfo;
 	gboolean ok, retval = TRUE;
 	CamelFolderChangeInfo *changes = NULL;
 
@@ -970,82 +1108,17 @@
 	summary_got = 0;
 	while ((type = camel_imap_command_response (store, &resp, ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED) 
 	{
-		GData *data;
-		char *uid;
-		guint32 flags, seq;
+		if (changes == NULL)
+			changes = camel_folder_change_info_new();
 
-		data = parse_fetch_response (imap_folder, resp);
-		g_free (resp);
-
-		if (!data)
-			continue;
-
-		uid = g_datalist_get_data (&data, "UID");
-		flags = GPOINTER_TO_UINT (g_datalist_get_data (&data, "FLAGS"));
-		seq = GPOINTER_TO_UINT (g_datalist_get_data (&data, "SEQUENCE"));
-
-		/* So basically: if the UID is not found locally, we have flags
-		 * for a new message. That's cool, but not an error as the 
-		 * camel_imap_folder_changed will launch imap_update_summary 
-		 * who will deal with that.
-		 * 
-		 * See, we wont find it if it's larger than the count of the
-		 * local summary list. We don't even have to check that, right? 
-		 * 
-		 * We also check for negative sequences. No reason for that, 
-		 * it's just wrong (actually impossible since it's a unsigned 
-		 * int, but it's just for security-clarity here. Let it be, it 
-		 * doesn't hurt either) */
-		
-		if (!uid /*|| seq < 0 */|| seq-1 > summary_len) {
+		if (process_condstore_line (imap_folder, resp, changes) == -1) {
 			imap_folder->need_rescan = TRUE;
-			retval = FALSE;
-			g_datalist_clear (&data);
+			g_free (resp);
 			continue;
 		}
 
-		info = camel_folder_summary_index (folder->summary, seq-1);
-		iinfo = (CamelImapMessageInfo *) info;
-		
-		
-		/* However, if we do find the message-info at that index then we 
-		 * we should also check whether the UID matches. If not, well ..
-		 * that's not good eh. That means that we shouldn't have been 
-		 * using this CONDSTORE code in the first place! Bad luck, we 
-		 * just set need_rescan TRUE and let the selected metod launch
-		 * the conventional imap_rescan later-on. Since we are receiving 
-		 * stuff anyway, and we need to read that away nonetheless, we
-		 * just continue doing our stuff. */
-		
-		if (info) 
-		{
-			/* printf ("%s vs %s on %d\n", info->uid, uid, seq-1); */
-			if (!strcmp (info->uid, uid))
-			{
-			  if (flags != iinfo->server_flags) 
-			  {
-				guint32 server_set, server_cleared;
-
-				camel_folder_summary_touch (folder->summary);
-				server_set = flags & ~iinfo->server_flags;
-				server_cleared = iinfo->server_flags & ~flags;
-				iinfo->info.flags = (iinfo->info.flags | server_set) & ~server_cleared;
-				iinfo->server_flags = flags;
-				if (changes == NULL)
-					changes = camel_folder_change_info_new();
-				camel_folder_change_info_change_uid(changes, uid);
-				/* flags_to_label(folder, (CamelImapMessageInfo *)info); */
-			  }
-			  camel_message_info_free (info);
-			} else {
-				imap_folder->need_rescan = TRUE;
-				retval = FALSE;
-			}
-		} else 
-			retval = FALSE;
-
+		g_free (resp);
 		camel_operation_progress (NULL, ++summary_got , summary_len);
-		g_datalist_clear (&data);
 	}
 
 	camel_operation_end (NULL);
@@ -1057,8 +1130,7 @@
 	if (resp)
 		g_free (resp);
 
-	if (changes) 
-	{
+	if (changes) {
 		camel_object_trigger_event(CAMEL_OBJECT (folder), "folder_changed", changes);
 		camel_folder_change_info_free(changes);
 	}
@@ -1074,7 +1146,7 @@
 	
 	if (!imap_folder->need_rescan)
 		camel_imap_folder_changed (folder, exists, NULL, ex);
-	
+
 	return retval;
 }
 
@@ -3952,7 +4024,38 @@
 	camel_folder_summary_save (folder->summary, ex);
 }
 
+
 static void
+camel_imap_folder_changed_for_uids (CamelFolder *folder, GPtrArray *expunged, CamelFolderChangeInfo *changes)
+{
+
+	if (expunged) 
+	{
+		CamelMessageInfo *info = NULL;
+		CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+		int i = 0;
+
+		for (i = 0; i < expunged->len; i++) 
+		{
+			char *uid = g_ptr_array_index (expunged, i);
+			info = camel_folder_summary_uid (folder->summary, uid);
+			if (info == NULL)
+				continue;
+			if (changes)
+				camel_folder_change_info_remove_uid (changes, camel_message_info_uid (info));
+			CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
+			camel_imap_message_cache_remove (imap_folder->cache, camel_message_info_uid (info));
+			CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
+			((CamelMessageInfoBase *)info)->flags |= CAMEL_MESSAGE_EXPUNGED;
+			camel_folder_summary_remove (folder->summary, info);
+			camel_message_info_free(info);
+		}
+	}
+
+	return;
+}
+
+static void
 do_nothing (void) { }
 
 static void
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c	(revision 2841)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c	(working copy)
@@ -46,6 +46,7 @@
 #include "camel-imap-store-summary.h"
 #include "camel-imap-store.h"
 #include "camel-imap-utils.h"
+#include "camel-imap-summary.h"
 
 extern int camel_verbose_debug;
 
@@ -63,7 +64,22 @@
 static char * imap_read_untagged_idle (CamelImapStore *store, char *line, CamelException *ex);
 
 
+static int
+uid_compar (const void *va, const void *vb)
+{
+	const char **sa = (const char **)va, **sb = (const char **)vb;
+	unsigned long a, b;
 
+	a = strtoul (*sa, NULL, 10);
+	b = strtoul (*sb, NULL, 10);
+	if (a < b)
+		return -1;
+	else if (a == b)
+		return 0;
+	else
+		return 1;
+}
+
 /**
  * camel_imap_command:
  * @store: the IMAP store
@@ -106,6 +122,8 @@
 		va_end (ap);
 	} else {
 
+		char *modseq = camel_imap_folder_get_highestmodseq (CAMEL_IMAP_FOLDER (folder));
+
 		/* camel_object_ref(folder); 
 		if (store->current_folder && CAMEL_IS_OBJECT (store->current_folder))
 			camel_object_unref(store->current_folder); */
@@ -117,10 +135,33 @@
 
 		store->current_folder = folder;
 
-		if (store->capabilities & IMAP_CAPABILITY_CONDSTORE) 
-			cmd = imap_command_strdup_printf (store, "SELECT %F (CONDSTORE)", folder->full_name);
-		else 
-			cmd = imap_command_strdup_printf (store, "SELECT %F", folder->full_name);
+		if (modseq && (store->capabilities & IMAP_CAPABILITY_QRESYNC))
+		{ 
+			CamelImapSummary *imap_summary = CAMEL_IMAP_SUMMARY (folder->summary);
+			GPtrArray *alluids = camel_folder_summary_array (folder->summary);
+			gchar *uidset = NULL;
+			gint lastuid;
+
+			qsort (alluids->pdata, alluids->len, sizeof (void *), uid_compar);
+
+			uidset = imap_uid_array_to_set (folder->summary, alluids, 0, -1, &lastuid);
+
+			camel_folder_summary_array_free (folder->summary, alluids);
+
+			cmd = imap_command_strdup_printf (store, "SELECT %F QRESYNC %d %s %s", 
+				folder->full_name, imap_summary->validity, modseq, uidset);
+
+			g_free (uidset);
+
+		} else {
+			if (store->capabilities & IMAP_CAPABILITY_CONDSTORE) 
+				cmd = imap_command_strdup_printf (store, "SELECT %F (CONDSTORE)", folder->full_name);
+			else 
+				cmd = imap_command_strdup_printf (store, "SELECT %F", folder->full_name);
+		}
+
+		if (modseq)
+			g_free (modseq);
 	}
 	
 	if (!imap_command_start (store, folder, cmd, ex)) {
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h	(revision 2841)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h	(working copy)
@@ -94,7 +94,9 @@
 void camel_imap_folder_stop_idle (CamelFolder *folder);
 void camel_imap_folder_start_idle (CamelFolder *folder);
 
+char* camel_imap_folder_get_highestmodseq (CamelImapFolder *imap_folder);
 
+
 G_END_DECLS
 
 #endif /* CAMEL_IMAP_FOLDER_H */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 2843)
+++ ChangeLog	(working copy)
@@ -1,3 +1,7 @@
+2007-10-18  Philip Van Hoof  <pvanhoof gnome org>
+
+	* Implemented QRESYNC 
+
 2007-10-17  Philip Van Hoof  <pvanhoof gnome org>
 
 	* Made sure that the standard Maildir format also works (with the ':'


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]