aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/imap/camel-imap-folder.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/providers/imap/camel-imap-folder.c')
-rw-r--r--camel/providers/imap/camel-imap-folder.c360
1 files changed, 254 insertions, 106 deletions
diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c
index 88b8e5f1b5..3984db4bd9 100644
--- a/camel/providers/imap/camel-imap-folder.c
+++ b/camel/providers/imap/camel-imap-folder.c
@@ -411,59 +411,77 @@ imap_rescan (CamelFolder *folder, int exists, CamelException *ex)
{
CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
- CamelImapResponse *response;
struct {
char *uid;
guint32 flags;
- } *new = NULL;
+ } *new;
char *resp;
- int i, seq, summary_len;
+ CamelImapResponseType type;
+ int i, seq, summary_len, summary_got;
CamelMessageInfo *info;
CamelImapMessageInfo *iinfo;
GArray *removed;
- GData *fetch_data;
- gpointer data;
+ gboolean ok;
CAMEL_IMAP_STORE_ASSERT_LOCKED (store, command_lock);
imap_folder->need_rescan = FALSE;
- camel_operation_start (NULL, _("Scanning IMAP folder"));
-
summary_len = camel_folder_summary_count (folder->summary);
- if (summary_len) {
- /* Check UIDs and flags of all messages we already know of. */
- info = camel_folder_summary_index (folder->summary, summary_len - 1);
- response = camel_imap_command (store, folder, ex,
- "UID FETCH 1:%s (FLAGS)",
- camel_message_info_uid (info));
- camel_folder_summary_info_free (folder->summary, info);
- if (!response) {
- camel_operation_end (NULL);
- return;
- }
+ if (summary_len == 0) {
+ if (exists)
+ camel_imap_folder_changed (folder, exists, NULL, ex);
+ return;
+ }
- new = g_malloc0 (summary_len * sizeof (*new));
- for (i = 0; i < response->untagged->len; i++) {
- resp = response->untagged->pdata[i];
+ /* Check UIDs and flags of all messages we already know of. */
+ camel_operation_start (NULL, _("Scanning for changed messages"));
+ info = camel_folder_summary_index (folder->summary, summary_len - 1);
+ ok = camel_imap_command_start (store, folder, ex,
+ "UID FETCH 1:%s (FLAGS)",
+ camel_message_info_uid (info));
+ camel_folder_summary_info_free (folder->summary, info);
+ if (!ok) {
+ camel_operation_end (NULL);
+ return;
+ }
- seq = strtoul (resp + 2, &resp, 10);
- if (g_strncasecmp (resp, " FETCH (", 8) != 0)
- continue;
- if (seq >= summary_len)
- continue;
+ new = g_malloc0 (summary_len * sizeof (*new));
+ summary_got = 0;
+ while ((type = camel_imap_command_response (store, &resp, ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED) {
+ GData *data;
+ char *uid;
+ guint32 flags;
- fetch_data = parse_fetch_response (imap_folder, resp + 7);
- data = g_datalist_get_data (&fetch_data, "UID");
- if (data && !new[seq - 1].uid)
- new[seq - 1].uid = g_strdup (data);
- data = g_datalist_get_data (&fetch_data, "FLAGS");
- if (data)
- new[seq - 1].flags = GPOINTER_TO_UINT (data);
- g_datalist_clear (&fetch_data);
+ 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;
}
- camel_imap_response_free_without_processing (store, response);
+
+ camel_operation_progress (NULL, ++summary_got * 100 / summary_len);
+ new[seq - 1].uid = g_strdup (uid);
+ new[seq - 1].flags = flags;
+ g_datalist_clear (&data);
}
+ camel_operation_end (NULL);
+ if (type == CAMEL_IMAP_RESPONSE_ERROR) {
+ for (i = 0; i < summary_len && new[i].uid; i++)
+ g_free (new[i].uid);
+ g_free (new);
+ return;
+ }
+ /* Free the final tagged response */
+ g_free (resp);
+
/* If we find a UID in the summary that doesn't correspond to
* the UID in the folder, then either: (a) it's a real UID,
* but the message was deleted on the server, or (b) it's a
@@ -521,8 +539,6 @@ imap_rescan (CamelFolder *folder, int exists, CamelException *ex)
/* And finally update the summary. */
camel_imap_folder_changed (folder, exists, removed, ex);
g_array_free (removed, TRUE);
-
- camel_operation_end (NULL);
}
/* Find all messages in @folder with flags matching @flags and @mask.
@@ -946,7 +962,7 @@ do_append (CamelFolder *folder, CamelMimeMessage *message,
/* send the rest of our data - the mime message */
g_byte_array_append (ba, "\0", 3);
- response = camel_imap_command_continuation (store, ex, ba->data);
+ response = camel_imap_command_continuation (store, ba->data, ex);
g_byte_array_free (ba, TRUE);
if (!response)
return response;
@@ -1536,110 +1552,226 @@ imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid,
camel_object_unref (CAMEL_OBJECT (stream));
}
+/* We pretend that a FLAGS or RFC822.SIZE response is always exactly
+ * 20 bytes long, and a BODY[HEADERS] response is always 2000 bytes
+ * long. Since we know how many of each kind of response we're
+ * expecting, we can find the total (pretend) amount of server traffic
+ * to expect and then count off the responses as we read them to update
+ * the progress bar.
+ */
+#define IMAP_PRETEND_SIZEOF_FLAGS 20
+#define IMAP_PRETEND_SIZEOF_SIZE 20
+#define IMAP_PRETEND_SIZEOF_HEADERS 2000
+
+static void
+add_message_from_data (CamelFolder *folder, GPtrArray *messages,
+ int first, GData *data)
+{
+ int seq;
+ CamelMimeMessage *msg;
+ CamelStream *stream;
+ CamelMessageInfo *mi;
+
+ seq = GPOINTER_TO_INT (g_datalist_get_data (&data, "SEQUENCE"));
+ if (seq < first)
+ return;
+ stream = g_datalist_get_data (&data, "BODY_PART_STREAM");
+ if (!stream)
+ return;
+
+ if (seq - first >= messages->len)
+ g_ptr_array_set_size (messages, seq - first + 1);
+
+ msg = camel_mime_message_new ();
+ camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream);
+ mi = camel_folder_summary_info_new_from_message (folder->summary, msg);
+ camel_object_unref (CAMEL_OBJECT (msg));
+
+ messages->pdata[seq - first] = mi;
+}
+
static void
-imap_update_summary (CamelFolder *folder,
+imap_update_summary (CamelFolder *folder, int exists,
CamelFolderChangeInfo *changes,
GPtrArray *recents,
CamelException *ex)
{
CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
- CamelImapResponse *response;
- GPtrArray *lines, *messages;
- char *p, *uid;
- int i, seq, first, exists = 0;
- CamelMimeMessage *msg;
+ CamelImapResponseType type;
+ GPtrArray *fetch_data = NULL, *messages = NULL, *needheaders;
+ char *uid, *resp;
+ const char *header_spec;
+ int i, seq, first, size, got, uidval;
CamelMessageInfo *mi;
- GData *fetch_data;
CamelStream *stream;
+ guint32 flags;
+ GData *data;
CAMEL_IMAP_STORE_ASSERT_LOCKED (store, command_lock);
+ if (store->server_level >= IMAP_LEVEL_IMAP4REV1)
+ header_spec = "HEADER";
+ else
+ header_spec = "0";
- first = camel_folder_summary_count (folder->summary) + 1;
+ /* Figure out if any of the new messages are already cached (which
+ * may be the case if we're re-syncing after disconnected operation).
+ * If so, get their UIDs, FLAGS, and SIZEs. If not, get all that
+ * and ask for the headers too at the same time.
+ */
+ seq = camel_folder_summary_count (folder->summary);
+ first = seq + 1;
+ if (seq > 0) {
+ mi = camel_folder_summary_index (folder->summary, seq - 1);
+ uidval = atoi (camel_message_info_uid (mi));
+ camel_folder_summary_info_free (folder->summary, mi);
+ } else
+ uidval = 0;
- response = camel_imap_command (store, folder, ex, "FETCH %d:* (UID FLAGS RFC822.SIZE)", first);
- if (!response)
- return;
+ size = (exists - seq) * (IMAP_PRETEND_SIZEOF_FLAGS + IMAP_PRETEND_SIZEOF_SIZE);
+ got = 0;
+
+ if (uidval >= camel_imap_message_cache_max_uid (imap_folder->cache)) {
+ /* None of the new messages are cached */
+ size += (exists - seq) * IMAP_PRETEND_SIZEOF_HEADERS;
+ if (!camel_imap_command_start (store, folder, ex,
+ "UID FETCH %d:* (FLAGS RFC822.SIZE BODY.PEEK[%s])",
+ uidval + 1, header_spec))
+ return;
+ camel_operation_start (NULL, _("Fetching summary information for new messages"));
+ } else {
+ if (!camel_imap_command_start (store, folder, ex,
+ "UID FETCH %d:* (FLAGS RFC822.SIZE)",
+ uidval + 1))
+ return;
+ camel_operation_start (NULL, _("Scanning for new messages"));
+ }
- /* Walk through the responses, looking for UIDs, and make sure
- * we have those headers cached.
+ /* Parse the responses. We can't add a message to the summary
+ * until we've gotten its headers, and there's no guarantee
+ * the server will send the responses in a useful order...
*/
+ fetch_data = g_ptr_array_new ();
messages = g_ptr_array_new ();
- lines = response->untagged;
- for (i = 0; i < lines->len; i++) {
- p = lines->pdata[i];
- if (*p++ != '*' || *p++ != ' ') {
- g_ptr_array_remove_index_fast (lines, i--);
+ while ((type = camel_imap_command_response (store, &resp, ex)) ==
+ CAMEL_IMAP_RESPONSE_UNTAGGED) {
+ data = parse_fetch_response (imap_folder, resp);
+ g_free (resp);
+ if (!data)
continue;
- }
- seq = strtoul (p, &p, 10);
- if (!g_strcasecmp (p, " EXISTS")) {
- exists = seq;
- g_ptr_array_remove_index_fast (lines, i--);
+
+ seq = GPOINTER_TO_INT (g_datalist_get_data (&data, "SEQUENCE"));
+ if (seq < first) {
+ g_datalist_clear (&data);
continue;
}
- if (!seq || seq < first || g_strncasecmp (p, " FETCH (", 8) != 0) {
- g_ptr_array_remove_index_fast (lines, i--);
- continue;
+
+ if (g_datalist_get_data (&data, "FLAGS"))
+ got += IMAP_PRETEND_SIZEOF_FLAGS;
+ if (g_datalist_get_data (&data, "RFC822.SIZE"))
+ got += IMAP_PRETEND_SIZEOF_SIZE;
+ stream = g_datalist_get_data (&data, "BODY_PART_STREAM");
+ if (stream) {
+ got += IMAP_PRETEND_SIZEOF_HEADERS;
+
+ /* Use the stream now so we don't tie up many
+ * many fds if we're fetching many many messages.
+ */
+ add_message_from_data (folder, messages, first, data);
+ g_datalist_set_data (&data, "BODY_PART_STREAM", NULL);
}
- if (seq - first >= messages->len)
- g_ptr_array_set_size (messages, seq - first + 1);
+ camel_operation_progress (NULL, got * 100 / size);
+ g_ptr_array_add (fetch_data, data);
+ }
+ camel_operation_end (NULL);
+
+ if (type == CAMEL_IMAP_RESPONSE_ERROR)
+ goto lose;
- fetch_data = parse_fetch_response (imap_folder, p + 7);
- uid = g_datalist_get_data (&fetch_data, "UID");
+ /* Figure out which headers we still need to fetch. */
+ needheaders = g_ptr_array_new ();
+ size = got = 0;
+ for (i = 0; i < fetch_data->len; i++) {
+ data = fetch_data->pdata[i];
+ if (g_datalist_get_data (&data, "BODY_PART_LEN"))
+ continue;
+
+ uid = g_datalist_get_data (&data, "UID");
if (uid) {
- stream = camel_imap_folder_fetch_data (
- imap_folder, uid,
- store->server_level >= IMAP_LEVEL_IMAP4REV1 ?
- "HEADER" : "0", FALSE, ex);
- if (!stream) {
- camel_imap_response_free_without_processing (store, response);
- /* XXX messages */
- return;
- }
+ g_ptr_array_add (needheaders, uid);
+ size += IMAP_PRETEND_SIZEOF_HEADERS;
+ }
+ }
- msg = camel_mime_message_new ();
- camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream);
- camel_object_unref (CAMEL_OBJECT (stream));
- mi = camel_folder_summary_info_new_from_message (folder->summary, msg);
- camel_object_unref (CAMEL_OBJECT (msg));
+ /* And fetch them */
+ if (needheaders->len) {
+ char *set;
- messages->pdata[seq - first] = mi;
+ /* FIXME: sort needheaders */
+ set = imap_uid_array_to_set (folder->summary, needheaders);
+ g_ptr_array_free (needheaders, TRUE);
+ if (!camel_imap_command_start (store, folder, ex,
+ "UID FETCH %s BODY.PEEK[%s]",
+ set, header_spec)) {
+ g_free (set);
+ goto lose;
}
- g_datalist_clear (&fetch_data);
+ g_free (set);
+
+ camel_operation_start (NULL, _("Fetching summary information for new messages"));
+ while ((type = camel_imap_command_response (store, &resp, ex))
+ == CAMEL_IMAP_RESPONSE_UNTAGGED) {
+ data = parse_fetch_response (imap_folder, resp);
+ g_free (resp);
+ if (!data)
+ continue;
+
+ stream = g_datalist_get_data (&data, "BODY_PART_STREAM");
+ if (stream) {
+ add_message_from_data (folder, messages, first, data);
+ got += IMAP_PRETEND_SIZEOF_HEADERS;
+ camel_operation_progress (NULL, got * 100 / size);
+ }
+ g_datalist_clear (&data);
+ }
+ camel_operation_end (NULL);
+
+ if (type == CAMEL_IMAP_RESPONSE_ERROR)
+ goto lose;
}
- /* Now go back through and create summary items */
- lines = response->untagged;
- for (i = 0; i < lines->len; i++) {
- p = lines->pdata[i];
- seq = strtoul (p + 2, &p, 10);
- p = strchr (p, '(');
+ /* Now finish up summary entries (fix UIDs, set flags and size) */
+ for (i = 0; i < fetch_data->len; i++) {
+ data = fetch_data->pdata[i];
- mi = messages->pdata[seq - first];
- if (!mi) /* ? */
+ seq = GPOINTER_TO_INT (g_datalist_get_data (&data, "SEQUENCE"));
+ if (seq >= first + messages->len) {
+ g_datalist_clear (&data);
continue;
- fetch_data = parse_fetch_response (imap_folder, p);
-
- if (g_datalist_get_data (&fetch_data, "UID"))
- camel_message_info_set_uid (mi, g_strdup (g_datalist_get_data (&fetch_data, "UID")));
- if (g_datalist_get_data (&fetch_data, "FLAGS")) {
- guint32 flags = GPOINTER_TO_INT (g_datalist_get_data (&fetch_data, "FLAGS"));
+ }
+ mi = messages->pdata[seq - first];
+ uid = g_datalist_get_data (&data, "UID");
+ if (uid)
+ camel_message_info_set_uid (mi, g_strdup (uid));
+ flags = GPOINTER_TO_INT (g_datalist_get_data (&data, "FLAGS"));
+ if (flags) {
((CamelImapMessageInfo *)mi)->server_flags = flags;
/* "or" them in with the existing flags that may
* have been set by summary_info_new_from_message.
*/
mi->flags |= flags;
}
- if (g_datalist_get_data (&fetch_data, "RFC822.SIZE"))
- mi->size = GPOINTER_TO_INT (g_datalist_get_data (&fetch_data, "RFC822.SIZE"));
+ size = GPOINTER_TO_INT (g_datalist_get_data (&data, "RFC822.SIZE"));
+ if (size)
+ mi->size = size;
- g_datalist_clear (&fetch_data);
+ g_datalist_clear (&data);
}
- camel_imap_response_free_without_processing (store, response);
+ g_ptr_array_free (fetch_data, TRUE);
+ /* And add the entries to the summary, etc. */
for (i = 0; i < messages->len; i++) {
mi = messages->pdata[i];
if (!mi) {
@@ -1648,14 +1780,28 @@ imap_update_summary (CamelFolder *folder,
}
camel_folder_summary_add (folder->summary, mi);
camel_folder_change_info_add_uid (changes, camel_message_info_uid (mi));
+
if (recents && (mi->flags & CAMEL_IMAP_MESSAGE_RECENT))
g_ptr_array_add (recents, (char *)camel_message_info_uid (mi));
}
g_ptr_array_free (messages, TRUE);
+ return;
- /* Did more mail arrive while we were doing this? */
- if (exists && exists > camel_folder_summary_count (folder->summary))
- imap_update_summary (folder, changes, recents, ex);
+ lose:
+ if (fetch_data) {
+ for (i = 0; i < fetch_data->len; i++) {
+ data = fetch_data->pdata[i];
+ g_datalist_clear (&data);
+ }
+ g_ptr_array_free (fetch_data, TRUE);
+ }
+ if (messages) {
+ for (i = 0; i < messages->len; i++) {
+ if (messages->pdata[i])
+ camel_folder_summary_info_free (folder->summary, messages->pdata[i]);
+ }
+ g_ptr_array_free (fetch_data, TRUE);
+ }
}
/* Called with the store's command_lock locked */
@@ -1697,7 +1843,7 @@ camel_imap_folder_changed (CamelFolder *folder, int exists,
if (exists > len) {
if (imap_folder->do_filtering)
recents = g_ptr_array_new ();
- imap_update_summary (folder, changes, recents, ex);
+ imap_update_summary (folder, exists, changes, recents, ex);
}
if (camel_folder_change_info_changed (changes)) {
@@ -1827,6 +1973,8 @@ parse_fetch_response (CamelImapFolder *imap_folder, char *response)
if (g_strncasecmp (response, " FETCH (", 8) != 0)
return NULL;
response += 7;
+
+ g_datalist_set_data (&data, "SEQUENCE", GINT_TO_POINTER (seq));
}
do {