CONDSTORE capability support for tinymail's camel-lite
- From: Philip Van Hoof <spam pvanhoof be>
- To: tinymail-devel-list gnome org, dave cridland net
- Subject: CONDSTORE capability support for tinymail's camel-lite
- Date: Fri, 26 Jan 2007 17:14:44 +0100
Well, it's not yet tested at all. I'm already posting this to have a
copy for myself when I will actually test this tomorrow.
This indeed needs some extensive testing. The removals are definitely
not yet right (CHANGEDSINCE doesn't give you the expunged ones as far as
I know). I'm probably going to checkout Polymer's code to figure out how
Dave did it ;-)
Again, it's not yet tested.
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h (revision 1479)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.h (working copy)
@@ -99,6 +99,7 @@
#define IMAP_CAPABILITY_XGWMOVE (1 << 10)
#define IMAP_CAPABILITY_LOGINDISABLED (1 << 11)
#define IMAP_CAPABILITY_CONDSTORE (1 << 12)
+#define IMAP_CAPABILITY_IDLE (1 << 13)
#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 1479)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c (working copy)
@@ -92,6 +92,7 @@
static void imap_finalize (CamelObject *object);
static int imap_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args);
+static void imap_rescan_condstore (CamelFolder *folder, int exists, const char *highestmodseq, CamelException *ex);
static void imap_rescan (CamelFolder *folder, int exists, CamelException *ex);
static void imap_refresh_info (CamelFolder *folder, CamelException *ex);
static void imap_sync_online (CamelFolder *folder, CamelException *ex);
@@ -295,6 +296,45 @@
return folder;
}
+
+static void
+put_highestmodseq (CamelImapFolder *imap_folder, const char *highestmodseq)
+{
+ char *filename = g_strdup_printf ("%s/status", imap_folder->folder_dir);
+ FILE *file;
+
+ file = fopen (filename, "w");
+ g_free (filename);
+
+ if (file != NULL)
+ {
+ fprintf (file, "%s", highestmodseq);
+ fclose (file);
+ }
+}
+
+static char*
+get_highestmodseq (CamelImapFolder *imap_folder)
+{
+ char *filename = g_strdup_printf ("%s/status", imap_folder->folder_dir);
+ /* max length in chars is that one, yes (the char values themselve
+ don't matter for this sizeof. It's just to reflect the RFC as-is) */
+ char *retval = NULL;
+ FILE *file;
+
+ file = fopen (filename, "r");
+ g_free (filename);
+
+ if (file != NULL)
+ {
+ retval = g_malloc0 (sizeof ("18446744073709551615"));
+ fscanf (file, "%s", &retval);
+ fclose (file);
+ }
+
+ return retval;
+}
+
/* Called with the store's connect_lock locked */
void
camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response,
@@ -307,10 +347,25 @@
guint32 perm_flags = 0;
GData *fetch_data;
int i, count;
- char *resp;
+ char *resp, *highestmodseq = NULL;
+ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
count = camel_folder_summary_count (folder->summary);
+
+/*
+ C: A142 SELECT INBOX (CONDSTORE)
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [HIGHESTMODSEQ 715194045007]
+ S: A142 OK [READ-WRITE] SELECT completed, CONDSTORE is now enabled
+*/
+
for (i = 0; i < response->untagged->len; i++) {
resp = response->untagged->pdata[i] + 2;
if (!g_ascii_strncasecmp (resp, "FLAGS ", 6) && !perm_flags) {
@@ -323,6 +378,33 @@
* even tho they do allow storing flags. *Sigh* So many fucking broken IMAP servers out there. */
if ((perm_flags = imap_parse_flag_list (&resp)) != 0)
folder->permanent_flags = perm_flags;
+ } else if (!g_ascii_strncasecmp (resp, "OK [HIGHESTMODSEQ ", sizeof ("OK [HIGHESTMODSEQ "))) {
+ char *marker;
+ unsigned int len;
+ resp += sizeof ("OK [HIGHESTMODSEQ ");
+
+ marker = strchr (resp, ']');
+
+ if (marker) {
+ char *phighestmodseq = NULL;
+
+ len = (unsigned int) (marker - resp);
+ highestmodseq = g_strndup (resp, len);
+
+ phighestmodseq = get_highestmodseq (imap_folder);
+ if (phighestmodseq && !strcmp (phighestmodseq, highestmodseq)) {
+ g_free (highestmodseq);
+ highestmodseq = NULL;
+ } else {
+ /* free highestmodseq later */
+ put_highestmodseq (imap_folder, (const char *) highestmodseq);
+ }
+
+ if (phighestmodseq)
+ g_free (phighestmodseq);
+ } else
+ highestmodseq = NULL;
+
} else if (!g_ascii_strncasecmp (resp, "OK [UIDVALIDITY ", 16)) {
validity = strtoul (resp + 16, NULL, 10);
} else if (isdigit ((unsigned char)*resp)) {
@@ -366,11 +448,18 @@
return;
}
+ if (highestmodseq != NULL && (store->capabilities & IMAP_CAPABILITY_CONDSTORE))
+ {
+ imap_rescan_condstore (folder, exists, highestmodseq, ex);
+
+ g_free (highestmodseq); highestmodseq = NULL;
+ imap_folder->need_rescan = FALSE;
+ } else {
+
/* If we've lost messages, we have to rescan everything */
- if (exists < count)
+ if (exists < count)
imap_folder->need_rescan = TRUE;
- else if (count != 0 && !imap_folder->need_rescan) {
- CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
+ else if (count != 0 && !imap_folder->need_rescan) {
/* Similarly, if the UID of the highest message we
* know about has changed, then that indicates that
@@ -408,8 +497,9 @@
camel_message_info_free(info);
if (uid == 0 || uid != val)
imap_folder->need_rescan = TRUE;
+ }
}
-
+
/* Now rescan if we need to */
if (imap_folder->need_rescan) {
imap_rescan (folder, exists, ex);
@@ -618,6 +708,125 @@
}
}
+static void
+imap_rescan_condstore (CamelFolder *folder, int exists, const char *highestmodseq, CamelException *ex)
+{
+
+/*
+ C: s100 UID FETCH 1:* (FLAGS) (CHANGEDSINCE 12345)
+ S: * 1 FETCH (UID 4 MODSEQ (65402) FLAGS (\Seen))
+ S: * 2 FETCH (UID 6 MODSEQ (75403) FLAGS (\Deleted))
+ S: * 4 FETCH (UID 8 MODSEQ (29738) FLAGS ($NoJunk $AutoJunk $MDNSent))
+ S: s100 OK FETCH completed
+*/
+
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
+ struct {
+ char *uid;
+ guint32 flags;
+ } *new;
+ char *resp;
+ CamelImapResponseType type;
+ int i, seq, summary_len, summary_got;
+ CamelMessageInfo *info;
+ CamelImapMessageInfo *iinfo;
+ GArray *removed;
+ gboolean ok;
+ CamelFolderChangeInfo *changes = NULL;
+
+ imap_folder->need_rescan = FALSE;
+
+ summary_len = camel_folder_summary_count (folder->summary);
+ if (summary_len == 0) {
+ if (exists)
+ camel_imap_folder_changed (folder, exists, NULL, ex);
+ return;
+ }
+
+ camel_operation_start (NULL, _("Scanning for changed messages in %s"), folder->name);
+ info = camel_folder_summary_index (folder->summary, summary_len - 1);
+ ok = camel_imap_command_start (store, folder, ex,
+ "UID FETCH 1:* (FLAGS) (CHANGEDSINCE %s)",
+ highestmodseq);
+ if (!ok) {
+ imap_folder->need_rescan = TRUE;
+ camel_operation_end (NULL);
+ return;
+ }
+
+ summary_got = 0;
+ while ((type = camel_imap_command_response (store, &resp, ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED)
+ {
+ GData *data;
+ char *uid;
+ guint32 flags;
+
+ data = parse_fetch_response (imap_folder, resp);
+ g_free (resp);
+
+ if (!data)
+ continue;
+
+ seq = GPOINTER_TO_INT (g_datalist_get_data (&data, "SEQUENCE"));
+ uid = g_datalist_get_data (&data, "UID");
+ flags = GPOINTER_TO_UINT (g_datalist_get_data (&data, "FLAGS"));
+
+ if (!uid || !seq || seq > summary_len) {
+ g_datalist_clear (&data);
+ continue;
+ }
+
+ info = camel_folder_summary_uid (folder->summary, uid);
+ iinfo = (CamelImapMessageInfo *) info;
+ if (info) {
+
+ if (flags != iinfo->server_flags) {
+ guint32 server_set, server_cleared;
+
+ 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, g_strdup (uid));
+ flags_to_label(folder, (CamelImapMessageInfo *)info);
+ }
+
+ camel_message_info_free (info);
+ } else {
+ camel_message_info_free(info);
+ g_array_append_val (removed, seq); /* TODO: check the value of seq! */
+ summary_len--;
+ }
+
+ camel_operation_progress (NULL, ++summary_got , summary_len);
+ g_datalist_clear (&data);
+ }
+
+ camel_operation_end (NULL);
+
+ if (type == CAMEL_IMAP_RESPONSE_ERROR)
+ return;
+
+ /* Free the final tagged response */
+ g_free (resp);
+
+ if (changes) {
+ camel_object_trigger_event(CAMEL_OBJECT (folder), "folder_changed", changes);
+ camel_folder_change_info_free(changes);
+ }
+
+
+ /* And finally update the summary. */
+ camel_imap_folder_changed (folder, exists, removed, ex);
+ g_array_free (removed, TRUE);
+}
+
+
/* Called with the store's connect_lock locked */
static void
imap_rescan (CamelFolder *folder, int exists, CamelException *ex)
@@ -654,6 +863,7 @@
camel_message_info_uid (info));
camel_message_info_free(info);
if (!ok) {
+ imap_folder->need_rescan = TRUE;
camel_operation_end (NULL);
return;
}
@@ -2640,6 +2850,7 @@
flags = GPOINTER_TO_INT (g_datalist_get_data (&data, "FLAGS"));
if (flags)
{
+ clear_normal_flags (mi);
mi->server_flags = flags;
mi->info.flags |= flags;
flags_to_label(folder, mi);
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c (revision 1479)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-command.c (working copy)
@@ -103,7 +103,10 @@
if (store->current_folder && CAMEL_IS_OBJECT (store->current_folder))
camel_object_unref(store->current_folder);
store->current_folder = folder;
- cmd = imap_command_strdup_printf (store, "SELECT %F", folder->full_name);
+ 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 (!imap_command_start (store, folder, cmd, ex)) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]