492 lines
16 KiB
Diff
492 lines
16 KiB
Diff
--- camel/camel-folder.c 2008-12-15 09:10:26 +0000
|
|
+++ camel/camel-folder.c 2008-12-16 06:57:35 +0000
|
|
@@ -88,6 +88,7 @@
|
|
CamelException *ex);
|
|
|
|
static GPtrArray *get_uids (CamelFolder *folder);
|
|
+static GPtrArray *get_uncached_uids (CamelFolder *, GPtrArray * uids, CamelException *);
|
|
static void free_uids (CamelFolder *folder,
|
|
GPtrArray *array);
|
|
static void sort_uids (CamelFolder *folder,
|
|
@@ -148,6 +149,7 @@
|
|
camel_folder_class->set_message_user_tag = set_message_user_tag;
|
|
camel_folder_class->get_message = get_message;
|
|
camel_folder_class->get_uids = get_uids;
|
|
+ camel_folder_class->get_uncached_uids = get_uncached_uids;
|
|
camel_folder_class->free_uids = free_uids;
|
|
camel_folder_class->sort_uids = sort_uids;
|
|
camel_folder_class->get_summary = get_summary;
|
|
@@ -163,6 +165,7 @@
|
|
camel_folder_class->delete = delete;
|
|
camel_folder_class->rename = folder_rename;
|
|
camel_folder_class->freeze = freeze;
|
|
+ camel_folder_class->sync_message = NULL;
|
|
camel_folder_class->thaw = thaw;
|
|
camel_folder_class->is_frozen = is_frozen;
|
|
camel_folder_class->get_quota_info = get_quota_info;
|
|
@@ -1137,6 +1140,35 @@
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * camel_folder_sync_message:
|
|
+ * @folder: a #CamelFolder object
|
|
+ * @uid: the UID
|
|
+ * @ex: a #CamelException
|
|
+ *
|
|
+ * Ensure that a message identified by UID has been synced in the folder (so
|
|
+ * that camel_folder_get_message on it later will work in offline mode).
|
|
+ *
|
|
+ * Returns: void.
|
|
+ **/
|
|
+void
|
|
+camel_folder_sync_message (CamelFolder *folder, const char *uid, CamelException *ex)
|
|
+{
|
|
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
|
|
+ CAMEL_FOLDER_REC_LOCK(folder, lock);
|
|
+
|
|
+ /* Use the sync_message method if the class implements it. */
|
|
+ if (CF_CLASS (folder)->sync_message)
|
|
+ CF_CLASS (folder)->sync_message (folder, uid, ex);
|
|
+ else {
|
|
+ CamelMimeMessage *message;
|
|
+ message = CF_CLASS (folder)->get_message (folder, uid, ex);
|
|
+ if (message)
|
|
+ camel_object_unref(message);
|
|
+ }
|
|
+ CAMEL_FOLDER_REC_UNLOCK(folder, lock);
|
|
+}
|
|
+
|
|
static GPtrArray *
|
|
get_uids(CamelFolder *folder)
|
|
{
|
|
@@ -1200,6 +1232,41 @@
|
|
}
|
|
|
|
|
|
+/**
|
|
+ * Default: return the uids we are given.
|
|
+ */
|
|
+static GPtrArray *
|
|
+get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
|
|
+{
|
|
+ GPtrArray *result;
|
|
+ int i;
|
|
+
|
|
+ result = g_ptr_array_new();
|
|
+
|
|
+ g_ptr_array_set_size(result, uids->len);
|
|
+ for (i = 0; i < uids->len; i++)
|
|
+ result->pdata[i] = (char *)camel_pstring_strdup(uids->pdata[i]);
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * camel_folder_get_uncached_uids:
|
|
+ * @folder: a #CamelFolder object
|
|
+ * @uids: the array of uids to filter down to uncached ones.
|
|
+ *
|
|
+ * Returns the known-uncached uids from a list of uids. It may return uids
|
|
+ * which are locally cached but should never filter out a uid which is not
|
|
+ * locally cached. Free the result by called #camel_folder_free_uids.
|
|
+ * Frees the array of UIDs returned by #camel_folder_get_uids.
|
|
+ **/
|
|
+GPtrArray *
|
|
+camel_folder_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
|
|
+{
|
|
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
|
|
+ return CF_CLASS (folder)->get_uncached_uids(folder, uids, ex);
|
|
+}
|
|
+
|
|
+
|
|
static int
|
|
uidcmp (const void *v0, const void *v1)
|
|
{
|
|
|
|
--- camel/camel-folder.h 2008-10-06 09:27:15 +0000
|
|
+++ camel/camel-folder.h 2008-12-16 06:57:35 +0000
|
|
@@ -211,6 +211,10 @@
|
|
|
|
CamelFolderQuotaInfo * (*get_quota_info) (CamelFolder *folder);
|
|
guint32 (*count_by_expression) (CamelFolder *, const char *, CamelException *);
|
|
+ void (*sync_message) (CamelFolder *folder,
|
|
+ const char *uid,
|
|
+ CamelException *ex);
|
|
+ GPtrArray * (*get_uncached_uids)(CamelFolder *, GPtrArray * uids, CamelException *);
|
|
} CamelFolderClass;
|
|
|
|
/* Standard Camel function */
|
|
@@ -303,12 +307,18 @@
|
|
CamelMimeMessage * camel_folder_get_message (CamelFolder *folder,
|
|
const char *uid,
|
|
CamelException *ex);
|
|
+void camel_folder_sync_message (CamelFolder *folder,
|
|
+ const char *uid,
|
|
+ CamelException *ex);
|
|
#define camel_folder_delete_message(folder, uid) \
|
|
camel_folder_set_message_flags (folder, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN)
|
|
|
|
GPtrArray * camel_folder_get_uids (CamelFolder *folder);
|
|
void camel_folder_free_uids (CamelFolder *folder,
|
|
GPtrArray *array);
|
|
+GPtrArray * camel_folder_get_uncached_uids (CamelFolder *,
|
|
+ GPtrArray * uids,
|
|
+ CamelException *);
|
|
void camel_folder_sort_uids (CamelFolder *folder,
|
|
GPtrArray *uids);
|
|
|
|
|
|
--- camel/camel-offline-folder.c 2008-06-20 22:32:12 +0000
|
|
+++ camel/camel-offline-folder.c 2008-12-16 06:57:35 +0000
|
|
@@ -247,8 +247,7 @@
|
|
offline_folder_downsync (CamelOfflineFolder *offline, const char *expression, CamelException *ex)
|
|
{
|
|
CamelFolder *folder = (CamelFolder *) offline;
|
|
- CamelMimeMessage *message;
|
|
- GPtrArray *uids;
|
|
+ GPtrArray *uids, *uncached_uids;
|
|
int i;
|
|
|
|
camel_operation_start (NULL, _("Syncing messages in folder '%s' to disk"), folder->full_name);
|
|
@@ -257,27 +256,29 @@
|
|
uids = camel_folder_search_by_expression (folder, expression, ex);
|
|
else
|
|
uids = camel_folder_get_uids (folder);
|
|
-
|
|
- if (!uids) {
|
|
- camel_operation_end (NULL);
|
|
- return;
|
|
+
|
|
+ if (!uids)
|
|
+ goto done;
|
|
+ uncached_uids = camel_folder_get_uncached_uids(folder, uids, ex);
|
|
+ if (uids) {
|
|
+ if (expression)
|
|
+ camel_folder_search_free (folder, uids);
|
|
+ else
|
|
+ camel_folder_free_uids (folder, uids);
|
|
}
|
|
|
|
- for (i = 0; i < uids->len; i++) {
|
|
- int pc = i * 100 / uids->len;
|
|
-
|
|
- message = camel_folder_get_message (folder, uids->pdata[i], ex);
|
|
+ if (!uncached_uids)
|
|
+ goto done;
|
|
+
|
|
+ for (i = 0; i < uncached_uids->len; i++) {
|
|
+ int pc = i * 100 / uncached_uids->len;
|
|
+ camel_folder_sync_message (folder, uncached_uids->pdata[i], ex);
|
|
camel_operation_progress (NULL, pc);
|
|
- if (message == NULL)
|
|
- break;
|
|
-
|
|
- camel_object_unref (message);
|
|
}
|
|
-
|
|
- if (expression)
|
|
- camel_folder_search_free (folder, uids);
|
|
- else
|
|
- camel_folder_free_uids (folder, uids);
|
|
+
|
|
+done:
|
|
+ if (uncached_uids)
|
|
+ camel_folder_free_uids(folder, uncached_uids);
|
|
|
|
camel_operation_end (NULL);
|
|
}
|
|
|
|
--- camel/providers/imap/camel-imap-folder.c 2008-12-05 12:10:12 +0000
|
|
+++ camel/providers/imap/camel-imap-folder.c 2008-12-16 06:57:35 +0000
|
|
@@ -102,10 +102,13 @@
|
|
static void imap_expunge (CamelFolder *folder, CamelException *ex);
|
|
//static void imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid, CamelException *ex);
|
|
static void imap_rename (CamelFolder *folder, const char *new);
|
|
+static GPtrArray * imap_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex);
|
|
|
|
/* message manipulation */
|
|
static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid,
|
|
CamelException *ex);
|
|
+static void imap_sync_message (CamelFolder *folder, const gchar *uid,
|
|
+ CamelException *ex);
|
|
static void imap_append_online (CamelFolder *folder, CamelMimeMessage *message,
|
|
const CamelMessageInfo *info, char **appended_uid,
|
|
CamelException *ex);
|
|
@@ -135,6 +138,12 @@
|
|
|
|
static GData *parse_fetch_response (CamelImapFolder *imap_folder, char *msg_att);
|
|
|
|
+/* internal helpers */
|
|
+static CamelImapMessageInfo * imap_folder_summary_uid_or_error(
|
|
+ CamelFolderSummary *summary,
|
|
+ const char * uid,
|
|
+ CamelException *ex);
|
|
+
|
|
#ifdef G_OS_WIN32
|
|
/* The strtok() in Microsoft's C library is MT-safe (but still uses
|
|
* only one buffer pointer per thread, but for the use of strtok_r()
|
|
@@ -166,7 +175,9 @@
|
|
camel_folder_class->expunge = imap_expunge;
|
|
camel_folder_class->sync= imap_sync;
|
|
camel_folder_class->append_message = imap_append_online;
|
|
+ camel_folder_class->sync_message = imap_sync_message;
|
|
camel_folder_class->transfer_messages_to = imap_transfer_online;
|
|
+ camel_folder_class->get_uncached_uids = imap_get_uncached_uids;
|
|
}
|
|
|
|
static void
|
|
@@ -2809,24 +2820,33 @@
|
|
return FALSE;
|
|
}
|
|
|
|
-static CamelMimeMessage *
|
|
-imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
|
|
+static CamelImapMessageInfo *
|
|
+imap_folder_summary_uid_or_error(CamelFolderSummary *summary, const char * uid, CamelException *ex)
|
|
{
|
|
- CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
|
|
- CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
|
|
- CamelImapMessageInfo *mi;
|
|
- CamelMimeMessage *msg = NULL;
|
|
- CamelStream *stream = NULL;
|
|
- int retry;
|
|
-
|
|
- mi = (CamelImapMessageInfo *)camel_folder_summary_uid (folder->summary, uid);
|
|
+ CamelImapMessageInfo *mi;
|
|
+ mi = (CamelImapMessageInfo *)camel_folder_summary_uid (summary, uid);
|
|
if (mi == NULL) {
|
|
camel_exception_setv (
|
|
ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
|
_("Cannot get message with message ID %s: %s"),
|
|
uid, _("No such message available."));
|
|
- return NULL;
|
|
}
|
|
+ return mi;
|
|
+}
|
|
+
|
|
+static CamelMimeMessage *
|
|
+imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
|
|
+{
|
|
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
|
|
+ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
|
|
+ CamelImapMessageInfo *mi;
|
|
+ CamelMimeMessage *msg = NULL;
|
|
+ CamelStream *stream = NULL;
|
|
+ int retry;
|
|
+
|
|
+ mi = imap_folder_summary_uid_or_error(folder->summary, uid, ex);
|
|
+ if (!mi)
|
|
+ return NULL;
|
|
|
|
/* If its cached in full, just get it as is, this is only a shortcut,
|
|
since we get stuff from the cache anyway. It affects a busted connection though. */
|
|
@@ -2947,6 +2967,44 @@
|
|
return msg;
|
|
}
|
|
|
|
+/**
|
|
+ * imap_sync_message
|
|
+ *
|
|
+ * Ensure that a message is cached locally, but don't retrieve the content if
|
|
+ * it is already local.
|
|
+ */
|
|
+static void
|
|
+imap_sync_message (CamelFolder *folder, const char *uid, CamelException *ex)
|
|
+{
|
|
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
|
|
+ CamelImapMessageInfo *mi;
|
|
+ CamelMimeMessage *msg = NULL;
|
|
+ CamelStream *stream = NULL;
|
|
+
|
|
+ mi = imap_folder_summary_uid_or_error(folder->summary, uid, ex);
|
|
+ if (!mi)
|
|
+ /* No such UID - is this duplicate work? The sync process selects
|
|
+ * UIDs to start with.
|
|
+ */
|
|
+ return;
|
|
+ camel_message_info_free(&mi->info);
|
|
+
|
|
+ /* If we can get a stream, assume its fully cached. This may be false
|
|
+ * if partial streams are saved elsewhere in the code - but that seems
|
|
+ * best solved by knowning more about whether a given message is fully
|
|
+ * available locally or not,
|
|
+ */
|
|
+ /* If its cached in full, just get it as is, this is only a shortcut,
|
|
+ since we get stuff from the cache anyway. It affects a busted connection though. */
|
|
+ if ((stream = camel_imap_folder_fetch_data(imap_folder, uid, "", TRUE, NULL))) {
|
|
+ camel_object_unref (stream);
|
|
+ return;
|
|
+ }
|
|
+ msg = imap_get_message(folder, uid, ex);
|
|
+ if (msg)
|
|
+ camel_object_unref(msg);
|
|
+}
|
|
+
|
|
/* FIXME Remove it after confirming
|
|
static void
|
|
imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid,
|
|
@@ -3919,3 +3977,19 @@
|
|
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
|
|
return res;
|
|
}
|
|
+
|
|
+/**
|
|
+ * Scan for messages that are local and return the rest.
|
|
+ */
|
|
+static GPtrArray *
|
|
+imap_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
|
|
+{
|
|
+ GPtrArray *result;
|
|
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
|
|
+
|
|
+ CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
|
|
+ result = camel_imap_message_cache_filter_cached (imap_folder->cache, uids, ex);
|
|
+ CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
|
|
+ return result;
|
|
+}
|
|
+
|
|
|
|
--- camel/providers/imap/camel-imap-message-cache.c 2008-10-16 03:42:34 +0000
|
|
+++ camel/providers/imap/camel-imap-message-cache.c 2008-12-16 06:57:35 +0000
|
|
@@ -35,6 +35,7 @@
|
|
#include "camel-data-wrapper.h"
|
|
#include "camel-exception.h"
|
|
#include "camel-stream-fs.h"
|
|
+#include "camel-string-utils.h"
|
|
|
|
#include "camel-imap-message-cache.h"
|
|
|
|
@@ -42,9 +43,27 @@
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
+/* Common define to start reducing duplication of base-part handling on win32.
|
|
+ */
|
|
+#ifdef G_OS_WIN32
|
|
+/* See comment in insert_setup() */
|
|
+#define BASE_PART_SUFFIX ".~"
|
|
+#else
|
|
+#define BASE_PART_SUFFIX "."
|
|
+#endif
|
|
+
|
|
static void finalize (CamelImapMessageCache *cache);
|
|
static void stream_finalize (CamelObject *stream, gpointer event_data, gpointer user_data);
|
|
|
|
+struct _part_find {
|
|
+ /* UID name on disk - e.g. "0." or "0.HEADERS". On windows "0." is
|
|
+ * stored as "0.~"
|
|
+ */
|
|
+ char *disk_part_name;
|
|
+ /* Was the part found? */
|
|
+ int found;
|
|
+};
|
|
+
|
|
|
|
CamelType
|
|
camel_imap_message_cache_get_type (void)
|
|
@@ -142,6 +161,8 @@
|
|
* 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.
|
|
+ * @path is scanned for its contents, which means creating a cache object can be
|
|
+ * expensive, but the parts hash is immediately usable.
|
|
**/
|
|
CamelImapMessageCache *
|
|
camel_imap_message_cache_new (const char *path, CamelFolderSummary *summary,
|
|
@@ -591,3 +612,67 @@
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+
|
|
+static void
|
|
+_match_part(gpointer part_name, gpointer user_data)
|
|
+{
|
|
+ struct _part_find *part_find = (struct _part_find *) user_data;
|
|
+ if (g_str_equal(part_name, part_find->disk_part_name))
|
|
+ part_find->found = 1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Filter uids by the uids cached in cache.
|
|
+ * The intent is that only uids fully cached are returned, but that may not be
|
|
+ * what is achieved. An additional constraint is that this check should be
|
|
+ * cheap, so that going offline is not an expensive operation. Filtering all
|
|
+ * uids is inefficient in the first place; significant processing per uid
|
|
+ * makes synchronisation very expensive. At the suggestion of Srinivasa Ragavan
|
|
+ * (see http://bugzilla.gnome.org/show_bug.cgi?id=564339) the cache->parts hash
|
|
+ * table is consulted. If there is a parts-list in the hash table containing
|
|
+ * the part "", then we assume the message has been completely downloaded. This
|
|
+ * is incorrect (see http://bugzilla.gnome.org/show_bug.cgi?id=561211 for the
|
|
+ * symptoms). The code this replaces, a loop over all uids asking for the ""
|
|
+ * part of the message has the same flaw: it is no /less/ accurate to assess
|
|
+ * 'cached' in the manner this method does (assuming no concurrent process is
|
|
+ * removing messages from the cache).
|
|
+ *
|
|
+ * In the future, fixing bug 561211 needs a check for *all* the parts of a
|
|
+ * given uid. If the complete list of parts is available in the folder summary
|
|
+ * information then it can be done cheaply, otherwise some redesign will be
|
|
+ * needed.
|
|
+ */
|
|
+GPtrArray *
|
|
+camel_imap_message_cache_filter_cached(CamelImapMessageCache *cache, GPtrArray *uids, CamelException *ex)
|
|
+{
|
|
+ GPtrArray *result, *parts_list;
|
|
+ int i;
|
|
+ struct _part_find part_find;
|
|
+ /* Look for a part "" for each uid. */
|
|
+ result = g_ptr_array_sized_new(uids->len);
|
|
+ for (i = 0; i < uids->len; i++) {
|
|
+ if ((parts_list = g_hash_table_lookup(cache->parts, uids->pdata[i]))) {
|
|
+ /* At least one part locally present; look for "" (the
|
|
+ * HEADERS part can be present without anything else,
|
|
+ * and that part is not useful for users wanting to
|
|
+ * read the message).
|
|
+ */
|
|
+ part_find.found = 0;
|
|
+ part_find.disk_part_name = g_strdup_printf("%s" BASE_PART_SUFFIX,
|
|
+ (char *)uids->pdata[i]);
|
|
+ g_ptr_array_foreach(parts_list, _match_part, &part_find);
|
|
+ g_free(part_find.disk_part_name);
|
|
+ if (part_find.found)
|
|
+ /* The message is cached locally, do not
|
|
+ * include it in the result.
|
|
+ */
|
|
+ continue;
|
|
+ }
|
|
+ /* No message parts, or message part "" not found: include the
|
|
+ * uid in the result.
|
|
+ */
|
|
+ g_ptr_array_add(result, (char *)camel_pstring_strdup(uids->pdata[i]));
|
|
+ }
|
|
+ return result;
|
|
+}
|
|
|
|
--- camel/providers/imap/camel-imap-message-cache.h 2008-07-31 09:31:51 +0000
|
|
+++ camel/providers/imap/camel-imap-message-cache.h 2008-12-16 06:57:35 +0000
|
|
@@ -41,6 +41,13 @@
|
|
CamelObject parent_object;
|
|
|
|
char *path;
|
|
+ /* parts contains two sorts of objects.
|
|
+ * If the key contains '.' then it is a stream (also reverse-indexed in
|
|
+ * cached).
|
|
+ * Otherwise it is a g_ptr_array containing the subparts the message
|
|
+ * has. (e.g. 0., or 0.MIME.1).
|
|
+ */
|
|
+ /* cached contains streams for recently accessed messages */
|
|
GHashTable *parts, *cached;
|
|
guint32 max_uid;
|
|
};
|
|
@@ -98,6 +105,9 @@
|
|
CamelException *ex);
|
|
gboolean camel_imap_message_cache_delete (const char *path,
|
|
CamelException *ex);
|
|
+GPtrArray * camel_imap_message_cache_filter_cached(CamelImapMessageCache *,
|
|
+ GPtrArray *uids,
|
|
+ CamelException *ex);
|
|
|
|
/* Standard Camel function */
|
|
CamelType camel_imap_message_cache_get_type (void);
|
|
|