aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/imap
diff options
context:
space:
mode:
authorDan Winship <danw@src.gnome.org>2001-05-01 22:51:36 +0800
committerDan Winship <danw@src.gnome.org>2001-05-01 22:51:36 +0800
commiteb7438182ab5fd969aac96795f67034e21937e35 (patch)
treeaa136f16d6be288b7623cb66a16d260944e61557 /camel/providers/imap
parentc757cacd08d3650309783172c2dedc0d20092634 (diff)
downloadgsoc2013-evolution-eb7438182ab5fd969aac96795f67034e21937e35.tar.gz
gsoc2013-evolution-eb7438182ab5fd969aac96795f67034e21937e35.tar.zst
gsoc2013-evolution-eb7438182ab5fd969aac96795f67034e21937e35.zip
Support the IMAP UIDPLUS extension (RFC 2359), which lets you
resync after disconnected operation more efficiently, but also makes it possible to do appends and moves/copies more efficiently now. * providers/imap/camel-imap-folder.c (imap_append_message): If the server supports UIDPLUS, grab the APPENDUID response and cache the message into the folder's message cache. (imap_copy_messages_to): Likewise, for COPYUID, copy any message parts we have cached between the source and destination folder caches. (imap_get_message): If the entire message is already in the cache, just return that rather than building it from parts. (imap_update_summary): Fetch just the "UID FLAGS RFC822.SIZE" of the new messages first, then only fetch the headers for messages where we don't already have the headers cached. * providers/imap/camel-imap-message-cache.c: Add gtk-doc comments. (cache_put): Fix refcounting stuff here. (camel_imap_message_cache_insert_stream, camel_imap_message_cache_insert_wrapper): New. (camel_imap_message_cache_get): Fix a bug here so the memory caching actually works. (camel_imap_message_cache_copy): New routine, used by imap_copy_messages_to. * providers/imap/camel-imap-utils.c (imap_uid_set_to_array): Inverse operation of imap_uid_array_to_set. Used to parse COPYUID response. svn path=/trunk/; revision=9635
Diffstat (limited to 'camel/providers/imap')
-rw-r--r--camel/providers/imap/camel-imap-folder.c173
-rw-r--r--camel/providers/imap/camel-imap-message-cache.c228
-rw-r--r--camel/providers/imap/camel-imap-message-cache.h14
-rw-r--r--camel/providers/imap/camel-imap-utils.c93
-rw-r--r--camel/providers/imap/camel-imap-utils.h2
5 files changed, 437 insertions, 73 deletions
diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c
index 89ac95fea5..ebfb34a0c0 100644
--- a/camel/providers/imap/camel-imap-folder.c
+++ b/camel/providers/imap/camel-imap-folder.c
@@ -642,8 +642,8 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message,
CamelMimeFilter *crlf_filter;
CamelStreamFilter *streamfilter;
GByteArray *ba;
- char *flagstr, *result;
-
+ char *flagstr, *result, *uid;
+
if (!camel_imap_store_check_online (store, ex))
return;
@@ -695,10 +695,82 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message,
CAMEL_IMAP_STORE_UNLOCK(store, command_lock);
if (!response)
return;
+
+ if (store->capabilities & IMAP_CAPABILITY_UIDPLUS) {
+ uid = strstrcase (response->status, "[APPENDUID ");
+ if (uid)
+ uid = strchr (uid + 11, ' ');
+ if (uid)
+ uid = g_strndup (uid + 1, strcspn (uid + 1, "]"));
+ if (uid) {
+ /* Make sure it's a number */
+ if (strtoul (uid, &result, 10) != 0 && !*result) {
+ /* OK. Cache the data. */
+ camel_imap_message_cache_insert_wrapper (
+ CAMEL_IMAP_FOLDER (folder)->cache,
+ uid, "", CAMEL_DATA_WRAPPER (message));
+ }
+ g_free (uid);
+ }
+ }
+
camel_imap_response_free (response);
}
static void
+handle_copyuid (CamelImapResponse *response, CamelFolder *source,
+ CamelFolder *destination)
+{
+ CamelImapMessageCache *scache = CAMEL_IMAP_FOLDER (source)->cache;
+ CamelImapMessageCache *dcache = CAMEL_IMAP_FOLDER (destination)->cache;
+ char *validity, *srcset, *destset;
+ GPtrArray *src, *dest;
+ int i;
+
+ validity = strstrcase (response->status, "[COPYUID ");
+ if (!validity)
+ return;
+ validity += 9;
+ if (strtoul (validity, NULL, 10) !=
+ CAMEL_IMAP_SUMMARY (destination->summary)->validity)
+ return;
+
+ srcset = strchr (validity, ' ');
+ if (!srcset++)
+ goto lose;
+ destset = strchr (srcset, ' ');
+ if (!destset++)
+ goto lose;
+
+ src = imap_uid_set_to_array (source->summary, srcset);
+ dest = imap_uid_set_to_array (destination->summary, destset);
+
+ if (src && dest && src->len == dest->len) {
+ /* We don't have to worry about deadlocking on the
+ * cache locks here, because we've got the store's
+ * command lock too, so no one else could be here.
+ */
+ CAMEL_IMAP_FOLDER_LOCK (source, cache_lock);
+ CAMEL_IMAP_FOLDER_LOCK (destination, cache_lock);
+ for (i = 0; i < src->len; i++) {
+ camel_imap_message_cache_copy (scache, src->pdata[i],
+ dcache, dest->pdata[i]);
+ }
+ CAMEL_IMAP_FOLDER_UNLOCK (source, cache_lock);
+ CAMEL_IMAP_FOLDER_UNLOCK (destination, cache_lock);
+
+ imap_uid_array_free (src);
+ imap_uid_array_free (dest);
+ return;
+ }
+
+ imap_uid_array_free (src);
+ imap_uid_array_free (dest);
+ lose:
+ g_warning ("Bad COPYUID response from server");
+}
+
+static void
imap_copy_messages_to (CamelFolder *source, GPtrArray *uids,
CamelFolder *destination, CamelException *ex)
{
@@ -719,7 +791,9 @@ imap_copy_messages_to (CamelFolder *source, GPtrArray *uids,
set = imap_uid_array_to_set (source->summary, uids);
response = camel_imap_command (store, source, ex, "UID COPY %s %S",
set, destination->full_name);
-
+ if (response && (store->capabilities & IMAP_CAPABILITY_UIDPLUS))
+ handle_copyuid (response, source, destination);
+
camel_imap_response_free (response);
g_free (set);
CAMEL_IMAP_STORE_UNLOCK(store, command_lock);
@@ -911,18 +985,21 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
CamelMessageInfo *mi;
CamelMimeMessage *msg;
- CamelStream *stream;
+ CamelStream *stream = NULL;
mi = camel_folder_summary_uid (folder->summary, uid);
g_return_val_if_fail (mi != NULL, NULL);
/* If the message is small, or the server doesn't support
- * IMAP4rev1, fetch it in one piece.
+ * IMAP4rev1, or we already have the whole thing cached,
+ * fetch it in one piece.
*/
if (mi->size < IMAP_SMALL_BODY_SIZE ||
- store->server_level < IMAP_LEVEL_IMAP4REV1) {
+ store->server_level < IMAP_LEVEL_IMAP4REV1 ||
+ (stream = camel_imap_folder_fetch_data (imap_folder, uid, "", TRUE, NULL))) {
camel_folder_summary_info_free (folder->summary, mi);
- stream = camel_imap_folder_fetch_data (imap_folder, uid, "", FALSE, ex);
+ if (!stream)
+ stream = camel_imap_folder_fetch_data (imap_folder, uid, "", FALSE, ex);
if (!stream)
return NULL;
msg = camel_mime_message_new ();
@@ -986,15 +1063,6 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
return msg;
}
-static const char *
-imap_protocol_get_summary_specifier (CamelImapStore *store)
-{
- if (store->server_level >= IMAP_LEVEL_IMAP4REV1)
- return "UID FLAGS RFC822.SIZE BODY.PEEK[HEADER]";
- else
- return "UID FLAGS RFC822.SIZE BODY.PEEK[0]";
-}
-
static void
imap_update_summary (CamelFolder *folder,
CamelFolderChangeInfo *changes,
@@ -1003,64 +1071,83 @@ imap_update_summary (CamelFolder *folder,
CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
CamelImapResponse *response;
- GPtrArray *headers, *messages;
+ GPtrArray *lines, *messages;
const char *summary_specifier;
- char *p;
+ char *p, *uid;
int i, seq, first, exists = 0;
CamelMimeMessage *msg;
CamelMessageInfo *mi;
GData *fetch_data;
+ CamelStream *stream;
first = camel_folder_summary_count (folder->summary) + 1;
summary_specifier = imap_protocol_get_summary_specifier (store);
/* We already have the command lock */
- response = camel_imap_command (store, folder, ex, "FETCH %d:* (%s)",
- first, summary_specifier);
+ response = camel_imap_command (store, folder, ex, "FETCH %d:* (UID FLAGS RFC822.SIZE)", first);
if (!response)
return;
+ /* Walk through the responses, looking for UIDs, and make sure
+ * we have those headers cached.
+ */
messages = g_ptr_array_new ();
- headers = response->untagged;
- for (i = 0; i < headers->len; i++) {
- p = headers->pdata[i];
- if (*p++ != '*' || *p++ != ' ')
+ 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--);
continue;
+ }
seq = strtoul (p, &p, 10);
if (!g_strcasecmp (p, " EXISTS")) {
exists = seq;
+ g_ptr_array_remove_index_fast (lines, i--);
continue;
}
- if (!seq || seq < first)
+ if (!seq || seq < first || g_strncasecmp (p, " FETCH (", 8) != 0) {
+ g_ptr_array_remove_index_fast (lines, i--);
continue;
- if (g_strncasecmp (p, " FETCH (", 8) != 0)
- continue;
- p += 7;
+ }
if (seq - first >= messages->len)
g_ptr_array_set_size (messages, seq - first + 1);
- mi = messages->pdata[seq - first];
- fetch_data = parse_fetch_response (imap_folder, p);
- if (!mi) {
- CamelStream *stream;
-
- if (!g_datalist_get_data (&fetch_data, "BODY_PART_DATA")) {
- g_datalist_clear (&fetch_data);
- p = headers->pdata[i];
- g_ptr_array_remove_index (headers, i--);
- g_ptr_array_add (headers, p);
- continue;
+ fetch_data = parse_fetch_response (imap_folder, p + 7);
+ uid = g_datalist_get_data (&fetch_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 (response);
+ /* XXX messages */
+ return;
}
msg = camel_mime_message_new ();
- stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM");
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));
messages->pdata[seq - first] = mi;
}
+ g_datalist_clear (&fetch_data);
+ }
+
+ /* 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, '(');
+
+ mi = messages->pdata[seq - first];
+ if (!mi) /* ? */
+ 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")));
@@ -1191,10 +1278,10 @@ camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid,
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
_("Could not find message body in FETCH "
"response."));
- } else
+ } else {
camel_object_ref (CAMEL_OBJECT (stream));
-
- g_datalist_clear (&fetch_data);
+ g_datalist_clear (&fetch_data);
+ }
return stream;
}
diff --git a/camel/providers/imap/camel-imap-message-cache.c b/camel/providers/imap/camel-imap-message-cache.c
index cd96409515..45f8d6db51 100644
--- a/camel/providers/imap/camel-imap-message-cache.c
+++ b/camel/providers/imap/camel-imap-message-cache.c
@@ -33,6 +33,7 @@
#include <string.h>
#include "camel-imap-message-cache.h"
+#include "camel-data-wrapper.h"
#include "camel-exception.h"
#include "camel-stream-fs.h"
@@ -92,18 +93,27 @@ cache_put (CamelImapMessageCache *cache, const char *uid, const char *key,
{
char *hash_key;
GPtrArray *subparts;
- gpointer old_key, old_value;
+ gpointer okey, ostream;
- hash_key = g_strdup (key);
subparts = g_hash_table_lookup (cache->parts, uid);
if (!subparts) {
subparts = g_ptr_array_new ();
g_hash_table_insert (cache->parts, g_strdup (uid), subparts);
- } else if (g_hash_table_lookup_extended (cache->parts, hash_key,
- &old_key, &old_value))
- g_ptr_array_remove (subparts, old_key);
+ }
+
+ if (g_hash_table_lookup_extended (cache->parts, key, &okey, &ostream)) {
+ if (ostream) {
+ camel_object_unhook_event (ostream, "finalize",
+ stream_finalize, cache);
+ g_hash_table_remove (cache->cached, ostream);
+ camel_object_unref (ostream);
+ }
+ hash_key = okey;
+ } else {
+ hash_key = g_strdup (key);
+ g_ptr_array_add (subparts, hash_key);
+ }
- g_ptr_array_add (subparts, hash_key);
g_hash_table_insert (cache->parts, hash_key, stream);
g_hash_table_insert (cache->cached, stream, hash_key);
@@ -113,6 +123,16 @@ cache_put (CamelImapMessageCache *cache, const char *uid, const char *key,
}
}
+/**
+ * camel_imap_message_cache_new:
+ * @path: directory to use for storage
+ * @summary: CamelFolderSummary for the folder we are caching
+ * @ex: a CamelException
+ *
+ * Return value: a new CamelImapMessageCache object using @path for
+ * storage. If cache files already exist in @path, then any that do not
+ * correspond to messages in @summary will be deleted.
+ **/
CamelImapMessageCache *
camel_imap_message_cache_new (const char *path, CamelFolderSummary *summary,
CamelException *ex)
@@ -187,44 +207,145 @@ stream_finalize (CamelObject *stream, gpointer event_data, gpointer user_data)
g_hash_table_insert (cache->parts, key, NULL);
}
-CamelStream *
-camel_imap_message_cache_insert (CamelImapMessageCache *cache, const char *uid,
- const char *part_spec, const char *data,
- int len)
+
+static CamelStream *
+insert_setup (CamelImapMessageCache *cache, const char *uid,
+ const char *part_spec, char **path, char **key)
{
- char *path, *key;
- int fd, status;
CamelStream *stream;
+ int fd;
- path = g_strdup_printf ("%s/%s.%s", cache->path, uid, part_spec);
- key = strrchr (path, '/') + 1;
- stream = g_hash_table_lookup (cache->parts, key);
+ *path = g_strdup_printf ("%s/%s.%s", cache->path, uid, part_spec);
+ *key = strrchr (*path, '/') + 1;
+ stream = g_hash_table_lookup (cache->parts, *key);
if (stream)
camel_object_unref (CAMEL_OBJECT (stream));
- fd = open (path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ fd = open (*path, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd == -1) {
- g_free (path);
+ g_free (*path);
return NULL;
}
- stream = camel_stream_fs_new_with_fd (fd);
- status = camel_stream_write (stream, data, len);
- camel_stream_reset (stream);
+ return camel_stream_fs_new_with_fd (fd);
+}
- if (status == -1) {
- unlink (path);
- g_free (path);
- camel_object_unref (CAMEL_OBJECT (stream));
- return NULL;
- }
+static CamelStream *
+insert_abort (char *path, CamelStream *stream)
+{
+ unlink (path);
+ g_free (path);
+ camel_object_unref (CAMEL_OBJECT (stream));
+ return NULL;
+}
+static CamelStream *
+insert_finish (CamelImapMessageCache *cache, const char *uid,
+ char *path, char *key, CamelStream *stream)
+{
+ camel_stream_reset (stream);
cache_put (cache, uid, key, stream);
+ printf ("caching %s\n", path);
g_free (path);
return stream;
}
+/**
+ * camel_imap_message_cache_insert:
+ * @cache: the cache
+ * @uid: UID of the message data to cache
+ * @part_spec: the IMAP part_spec of the data
+ * @data: the data
+ * @len: length of @data
+ *
+ * Caches the provided data into @cache.
+ *
+ * Return value: a CamelStream containing the cached data, which the
+ * caller must unref.
+ **/
+CamelStream *
+camel_imap_message_cache_insert (CamelImapMessageCache *cache, const char *uid,
+ const char *part_spec, const char *data,
+ int len)
+{
+ char *path, *key;
+ CamelStream *stream;
+
+ stream = insert_setup (cache, uid, part_spec, &path, &key);
+ if (!stream)
+ return NULL;
+ if (camel_stream_write (stream, data, len) == -1)
+ return insert_abort (path, stream);
+ return insert_finish (cache, uid, path, key, stream);
+}
+
+/**
+ * camel_imap_message_cache_insert_stream:
+ * @cache: the cache
+ * @uid: UID of the message data to cache
+ * @part_spec: the IMAP part_spec of the data
+ * @data_stream: the stream to cache
+ *
+ * Caches the provided data into @cache.
+ **/
+void
+camel_imap_message_cache_insert_stream (CamelImapMessageCache *cache,
+ const char *uid, const char *part_spec,
+ CamelStream *data_stream)
+{
+ char *path, *key;
+ CamelStream *stream;
+
+ stream = insert_setup (cache, uid, part_spec, &path, &key);
+ if (!stream)
+ return;
+ if (camel_stream_write_to_stream (data_stream, stream) == -1)
+ insert_abort (path, stream);
+ else {
+ insert_finish (cache, uid, path, key, stream);
+ camel_object_unref (CAMEL_OBJECT (stream));
+ }
+}
+
+/**
+ * camel_imap_message_cache_insert_wrapper:
+ * @cache: the cache
+ * @uid: UID of the message data to cache
+ * @part_spec: the IMAP part_spec of the data
+ * @wrapper: the wrapper to cache
+ *
+ * Caches the provided data into @cache.
+ **/
+void
+camel_imap_message_cache_insert_wrapper (CamelImapMessageCache *cache,
+ const char *uid, const char *part_spec,
+ CamelDataWrapper *wrapper)
+{
+ char *path, *key;
+ CamelStream *stream;
+
+ stream = insert_setup (cache, uid, part_spec, &path, &key);
+ if (!stream)
+ return;
+ if (camel_data_wrapper_write_to_stream (wrapper, stream) == -1)
+ insert_abort (path, stream);
+ else {
+ insert_finish (cache, uid, path, key, stream);
+ camel_object_unref (CAMEL_OBJECT (stream));
+ }
+}
+
+
+/**
+ * camel_imap_message_cache_get:
+ * @cache: the cache
+ * @uid: the UID of the data to get
+ * @part_spec: the part_spec of the data to get
+ *
+ * Return value: a CamelStream containing the cached data (which the
+ * caller must unref), or %NULL if that data is not cached.
+ **/
CamelStream *
camel_imap_message_cache_get (CamelImapMessageCache *cache, const char *uid,
const char *part_spec)
@@ -233,7 +354,7 @@ camel_imap_message_cache_get (CamelImapMessageCache *cache, const char *uid,
char *path, *key;
path = g_strdup_printf ("%s/%s.%s", cache->path, uid, part_spec);
- key = strrchr (path, '/');
+ key = strrchr (path, '/') + 1;
stream = g_hash_table_lookup (cache->parts, key);
if (stream) {
camel_object_ref (CAMEL_OBJECT (stream));
@@ -241,13 +362,22 @@ camel_imap_message_cache_get (CamelImapMessageCache *cache, const char *uid,
}
stream = camel_stream_fs_new_with_name (path, O_RDONLY, 0);
- if (stream)
+ if (stream) {
+ printf ("got %s\n", path);
cache_put (cache, uid, key, stream);
+ }
g_free (path);
return stream;
}
+/**
+ * camel_imap_message_cache_remove:
+ * @cache: the cache
+ * @uid: UID of the data to remove
+ *
+ * Removes all data associated with @uid from @cache.
+ **/
void
camel_imap_message_cache_remove (CamelImapMessageCache *cache, const char *uid)
{
@@ -286,8 +416,50 @@ clear_part (gpointer key, gpointer value, gpointer data)
return TRUE;
}
+/**
+ * camel_imap_message_cache_clear:
+ * @cache: the cache
+ *
+ * Removes all cached data from @cache.
+ **/
void
camel_imap_message_cache_clear (CamelImapMessageCache *cache)
{
g_hash_table_foreach_remove (cache->parts, clear_part, cache);
}
+
+
+/**
+ * camel_imap_message_cache_copy:
+ * @source: the source message cache
+ * @source_uid: UID of a message in @source
+ * @dest: the destination message cache
+ * @dest_uid: UID of the message in @dest
+ *
+ * Copies all cached parts from @source_uid in @source to @dest_uid in
+ * @destination.
+ **/
+void
+camel_imap_message_cache_copy (CamelImapMessageCache *source,
+ const char *source_uid,
+ CamelImapMessageCache *dest,
+ const char *dest_uid)
+{
+ GPtrArray *subparts;
+ CamelStream *stream;
+ char *part;
+ int i;
+
+ subparts = g_hash_table_lookup (source->parts, source_uid);
+ if (!subparts || !subparts->len)
+ return;
+
+ for (i = 0; i < subparts->len; i++) {
+ part = strchr (subparts->pdata[i], '.');
+ if (!part++)
+ continue;
+ stream = camel_imap_message_cache_get (source, source_uid, part);
+ camel_imap_message_cache_insert_stream (dest, dest_uid, part, stream);
+ camel_object_unref (CAMEL_OBJECT (stream));
+ }
+}
diff --git a/camel/providers/imap/camel-imap-message-cache.h b/camel/providers/imap/camel-imap-message-cache.h
index 66e3adbaaa..c9af369a56 100644
--- a/camel/providers/imap/camel-imap-message-cache.h
+++ b/camel/providers/imap/camel-imap-message-cache.h
@@ -68,6 +68,15 @@ CamelStream *camel_imap_message_cache_insert (CamelImapMessageCache *cache,
const char *part_spec,
const char *data,
int len);
+void camel_imap_message_cache_insert_stream (CamelImapMessageCache *cache,
+ const char *uid,
+ const char *part_spec,
+ CamelStream *data_stream);
+void camel_imap_message_cache_insert_wrapper (CamelImapMessageCache *cache,
+ const char *uid,
+ const char *part_spec,
+ CamelDataWrapper *wrapper);
+
CamelStream *camel_imap_message_cache_get (CamelImapMessageCache *cache,
const char *uid,
const char *part_spec);
@@ -76,6 +85,11 @@ void camel_imap_message_cache_remove (CamelImapMessageCache *cache,
void camel_imap_message_cache_clear (CamelImapMessageCache *cache);
+void camel_imap_message_cache_copy (CamelImapMessageCache *source,
+ const char *source_uid,
+ CamelImapMessageCache *dest,
+ const char *dest_uid);
+
/* Standard Camel function */
CamelType camel_imap_message_cache_get_type (void);
diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c
index 22f3c95b50..59d6ac5ea8 100644
--- a/camel/providers/imap/camel-imap-utils.c
+++ b/camel/providers/imap/camel-imap-utils.c
@@ -611,7 +611,7 @@ imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids)
if (++si < scount)
next_summary_uid = get_summary_uid_numeric (summary, si);
else
- next_summary_uid = (guint32) -1;
+ next_summary_uid = (unsigned long) -1;
/* Now get the next UID from @uids */
this_uid = strtoul (uids->pdata[ui], NULL, 10);
@@ -620,7 +620,7 @@ imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids)
if (++si < scount)
next_summary_uid = get_summary_uid_numeric (summary, si);
else
- next_summary_uid = (guint32) -1;
+ next_summary_uid = (unsigned long) -1;
} else {
if (range) {
g_string_sprintfa (gset, ":%lu", last_uid);
@@ -640,3 +640,92 @@ imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids)
return set;
}
+
+/**
+ * imap_uid_set_to_array:
+ * @summary: summary for the folder the UIDs come from
+ * @uids: a pointer to the start of an IMAP "set" of UIDs
+ *
+ * Fills an array with the UIDs corresponding to @uids and @summary.
+ * There can be text after the uid set in @uids, which will be
+ * ignored.
+ *
+ * If @uids specifies a range of UIDs that extends outside the range
+ * of @summary, the function will assume that all of the "missing" UIDs
+ * do exist.
+ *
+ * Return value: the array of uids, which the caller must free with
+ * imap_uid_array_free(). (Or %NULL if the uid set can't be parsed.)
+ **/
+GPtrArray *
+imap_uid_set_to_array (CamelFolderSummary *summary, const char *uids)
+{
+ GPtrArray *arr;
+ char *p, *q;
+ unsigned long uid, suid;
+ int si, scount;
+
+ arr = g_ptr_array_new ();
+ scount = camel_folder_summary_count (summary);
+
+ p = (char *)uids;
+ si = 0;
+ do {
+ uid = strtoul (p, &q, 10);
+ if (p == q)
+ goto lose;
+ g_ptr_array_add (arr, g_strndup (p, q - p));
+
+ if (*q == ':') {
+ /* Find the summary entry for the UID after the one
+ * we just saw.
+ */
+ while (++si < scount) {
+ suid = get_summary_uid_numeric (summary, si);
+ if (suid > uid)
+ break;
+ }
+ if (si >= scount)
+ suid = uid + 1;
+
+ uid = strtoul (q + 1, &p, 10);
+ if (p == q + 1)
+ goto lose;
+
+ /* Add each summary UID until we find one
+ * larger than the end of the range
+ */
+ while (suid <= uid) {
+ g_ptr_array_add (arr, g_strdup_printf ("%lu", suid));
+ if (++si < scount)
+ suid = get_summary_uid_numeric (summary, si);
+ else
+ suid++;
+ }
+ } else
+ p = q;
+ } while (*p++ == ',');
+
+ return arr;
+
+ lose:
+ g_warning ("Invalid uid set %s", uids);
+ imap_uid_array_free (arr);
+ return NULL;
+}
+
+/**
+ * imap_uid_array_free:
+ * @arr: an array returned from imap_uid_set_to_array()
+ *
+ * Frees @arr
+ **/
+void
+imap_uid_array_free (GPtrArray *arr)
+{
+ int i;
+
+ for (i = 0; i < arr->len; i++)
+ g_free (arr->pdata[i]);
+ g_ptr_array_free (arr, TRUE);
+}
diff --git a/camel/providers/imap/camel-imap-utils.h b/camel/providers/imap/camel-imap-utils.h
index 937bec02d1..583505a95c 100644
--- a/camel/providers/imap/camel-imap-utils.h
+++ b/camel/providers/imap/camel-imap-utils.h
@@ -58,6 +58,8 @@ char *imap_quote_string (const char *str);
void imap_skip_list (char **str_p);
char * imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids);
+GPtrArray *imap_uid_set_to_array (CamelFolderSummary *summary, const char *uids);
+void imap_uid_array_free (GPtrArray *arr);
#ifdef __cplusplus
}