diff options
Diffstat (limited to 'camel')
-rw-r--r-- | camel/providers/imap/camel-imap-command.c | 14 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-folder.c | 483 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-message-cache.c | 5 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.c | 31 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-summary.c | 30 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-summary.h | 5 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.c | 11 |
7 files changed, 507 insertions, 72 deletions
diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index 4102d47cc5..54df2fe5a3 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -182,9 +182,11 @@ imap_read_response (CamelImapStore *store, CamelException *ex) } response = g_new0 (CamelImapResponse, 1); - response->folder = store->current_folder; - if (response->folder) - camel_object_ref (CAMEL_OBJECT (response->folder)); + if (camel_disco_store_status (CAMEL_DISCO_STORE (store)) != CAMEL_DISCO_STORE_RESYNCING) { + response->folder = store->current_folder; + if (response->folder) + camel_object_ref (CAMEL_OBJECT (response->folder)); + } response->untagged = g_ptr_array_new (); /* Check for untagged data */ @@ -209,7 +211,7 @@ imap_read_response (CamelImapStore *store, CamelException *ex) } if (!respbuf || camel_exception_is_set (ex)) { - camel_imap_response_free (store, response); + camel_imap_response_free_without_processing (store, response); return NULL; } @@ -231,7 +233,7 @@ imap_read_response (CamelImapStore *store, CamelException *ex) camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, _("Unexpected response from IMAP " "server: %s"), respbuf); - camel_imap_response_free (store, response); + camel_imap_response_free_without_processing (store, response); return NULL; } @@ -239,7 +241,7 @@ imap_read_response (CamelImapStore *store, CamelException *ex) camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, _("IMAP command failed: %s"), retcode ? retcode : _("Unknown error")); - camel_imap_response_free (store, response); + camel_imap_response_free_without_processing (store, response); return NULL; } diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index b8e53c604a..abced473f6 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -46,21 +46,22 @@ #include "camel-imap-summary.h" #include "camel-imap-utils.h" #include "camel-imap-wrapper.h" -#include "string-utils.h" -#include "camel-session.h" -#include "camel-stream.h" -#include "camel-stream-mem.h" -#include "camel-stream-buffer.h" #include "camel-data-wrapper.h" -#include "camel-mime-message.h" -#include "camel-stream-filter.h" -#include "camel-mime-filter-from.h" -#include "camel-mime-filter-crlf.h" +#include "camel-disco-diary.h" #include "camel-exception.h" -#include "camel-mime-utils.h" #include "camel-imap-private.h" +#include "camel-mime-filter-crlf.h" +#include "camel-mime-filter-from.h" +#include "camel-mime-message.h" +#include "camel-mime-utils.h" #include "camel-multipart.h" #include "camel-operation.h" +#include "camel-session.h" +#include "camel-stream-buffer.h" +#include "camel-stream-filter.h" +#include "camel-stream-mem.h" +#include "camel-stream.h" +#include "string-utils.h" #define CF_CLASS(o) (CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(o))) static CamelDiscoFolderClass *disco_folder_class = NULL; @@ -73,19 +74,25 @@ static void imap_sync_offline (CamelFolder *folder, CamelException *ex); static const char *imap_get_full_name (CamelFolder *folder); static void imap_expunge_uids_online (CamelFolder *folder, GPtrArray *uids, CamelException *ex); static void imap_expunge_uids_offline (CamelFolder *folder, GPtrArray *uids, CamelException *ex); +static void imap_expunge_uids_resyncing (CamelFolder *folder, GPtrArray *uids, CamelException *ex); static void imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid, CamelException *ex); /* message manipulation */ static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex); -static char *imap_append_online (CamelFolder *folder, CamelMimeMessage *message, +static void imap_append_online (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex); -static char *imap_append_offline (CamelFolder *folder, CamelMimeMessage *message, +static void imap_append_offline (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex); +static void imap_append_resyncing (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, CamelException *ex); + static void imap_copy_online (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); static void imap_copy_offline (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); +static void imap_copy_resyncing (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex); static void imap_move_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); @@ -113,12 +120,19 @@ camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class) camel_disco_folder_class->refresh_info_online = imap_refresh_info; camel_disco_folder_class->sync_online = imap_sync_online; camel_disco_folder_class->sync_offline = imap_sync_offline; + /* We don't sync flags at resync time: the online code will + * deal with it eventually. + */ + camel_disco_folder_class->sync_resyncing = imap_sync_offline; camel_disco_folder_class->expunge_uids_online = imap_expunge_uids_online; camel_disco_folder_class->expunge_uids_offline = imap_expunge_uids_offline; + camel_disco_folder_class->expunge_uids_resyncing = imap_expunge_uids_resyncing; camel_disco_folder_class->append_online = imap_append_online; camel_disco_folder_class->append_offline = imap_append_offline; + camel_disco_folder_class->append_resyncing = imap_append_resyncing; camel_disco_folder_class->copy_online = imap_copy_online; camel_disco_folder_class->copy_offline = imap_copy_offline; + camel_disco_folder_class->copy_resyncing = imap_copy_resyncing; camel_disco_folder_class->cache_message = imap_cache_message; } @@ -247,6 +261,17 @@ camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response, } } + if (camel_disco_store_status (CAMEL_DISCO_STORE (folder->parent_store)) == CAMEL_DISCO_STORE_RESYNCING) { + if (validity != imap_summary->validity) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID, + _("Folder was destroyed and recreated on server.")); + return; + } + + /* FIXME: find missing UIDs ? */ + return; + } + if (!imap_summary->validity) imap_summary->validity = validity; else if (validity != imap_summary->validity) { @@ -414,8 +439,11 @@ imap_rescan (CamelFolder *folder, int exists, CamelException *ex) } /* If we find a UID in the summary that doesn't correspond to - * the UID in the folder, that it means the message was - * deleted on the server, so we remove it from the summary. + * 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 + * fake UID, and needs to be removed from the summary in order + * to sync up with the server. So either way, we remove it + * from the summary. */ removed = g_array_new (FALSE, FALSE, sizeof (int)); summary_len = camel_folder_summary_count (folder->summary); @@ -432,6 +460,7 @@ imap_rescan (CamelFolder *folder, int exists, CamelException *ex) iinfo = (CamelImapMessageInfo *)info; if (strcmp (camel_message_info_uid (info), new[i].uid) != 0) { + camel_folder_summary_info_free(folder->summary, info); seq = i + 1; g_array_append_val (removed, seq); i--; @@ -609,8 +638,18 @@ imap_sync_online (CamelFolder *folder, CamelException *ex) static void imap_expunge_uids_offline (CamelFolder *folder, GPtrArray *uids, CamelException *ex) { - /* Fail */ - camel_disco_store_check_online (CAMEL_DISCO_STORE (folder->parent_store), ex); + int i; + + for (i = 0; i < uids->len; i++) { + camel_folder_summary_remove_uid (folder->summary, uids->pdata[i]); + /* We intentionally don't remove it from the cache because + * the cached data may be useful in replaying a COPY later. + */ + } + camel_folder_summary_save (folder->summary); + + camel_disco_diary_log (CAMEL_DISCO_STORE (folder->parent_store)->diary, + CAMEL_DISCO_DIARY_FOLDER_EXPUNGE, folder, uids); } static void @@ -643,6 +682,148 @@ imap_expunge_uids_online (CamelFolder *folder, GPtrArray *uids, CamelException * CAMEL_IMAP_STORE_UNLOCK (store, command_lock); } +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; +} + +static void +imap_expunge_uids_resyncing (CamelFolder *folder, GPtrArray *uids, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; + char *result, *keep_uidset, *mark_uidset; + + if (store->capabilities & IMAP_CAPABILITY_UIDPLUS) { + imap_expunge_uids_online (folder, uids, ex); + return; + } + + /* If we don't have UID EXPUNGE we need to avoid expunging any + * of the wrong messages. So we search for deleted messages, + * and any that aren't in our to-expunge list get temporarily + * marked un-deleted. + */ + + CAMEL_IMAP_STORE_LOCK (store, command_lock); + response = camel_imap_command (store, folder, ex, "UID SEARCH DELETED"); + if (!response) { + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return; + } + result = camel_imap_response_extract (store, response, "SEARCH", ex); + if (!result) { + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return; + } + + keep_uidset = mark_uidset = NULL; + if (result[8] == ' ') { + GPtrArray *keep_uids, *mark_uids; + char *uid, *lasts = NULL; + unsigned long euid, kuid; + int ei, ki; + + keep_uids = g_ptr_array_new (); + mark_uids = g_ptr_array_new (); + + /* Parse SEARCH response */ + for (uid = strtok_r (result + 9, " ", &lasts); uid; uid = strtok_r (NULL, " ", &lasts)) + g_ptr_array_add (keep_uids, uid); + qsort (keep_uids->pdata, keep_uids->len, + sizeof (void *), uid_compar); + + /* Fill in "mark_uids", empty out "keep_uids" as needed */ + for (ei = ki = 0; ei < uids->len; ei++) { + euid = strtoul (uids->pdata[ei], NULL, 10); + + for (; ki < keep_uids->len; ki++) { + kuid = strtoul (keep_uids->pdata[ki], NULL, 10); + + if (kuid >= euid) + break; + } + + if (euid == kuid) + g_ptr_array_remove_index (keep_uids, ki); + else + g_ptr_array_add (mark_uids, uids->pdata[ei]); + } + + if (keep_uids->len) + keep_uidset = imap_uid_array_to_set (folder->summary, keep_uids); + g_ptr_array_free (keep_uids, TRUE); + + if (mark_uids->len) + mark_uidset = imap_uid_array_to_set (folder->summary, mark_uids); + g_ptr_array_free (mark_uids, TRUE); + } else { + /* Empty SEARCH result, meaning nothing is marked deleted + * on server. + */ + mark_uidset = imap_uid_array_to_set (folder->summary, uids); + } + g_free (result); + + /* Unmark messages to be kept */ + if (keep_uidset) { + response = camel_imap_command (store, folder, ex, + "UID STORE %s -FLAGS.SILENT \\Deleted", + keep_uidset); + if (!response) { + g_free (keep_uidset); + g_free (mark_uidset); + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return; + } + camel_imap_response_free (store, response); + } + + /* Mark any messages that still need to be marked */ + if (mark_uidset) { + response = camel_imap_command (store, folder, ex, + "UID STORE %s +FLAGS.SILENT \\Deleted", + mark_uidset); + g_free (mark_uidset); + if (!response) { + g_free (keep_uidset); + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return; + } + camel_imap_response_free (store, response); + } + + /* Do the actual expunging */ + response = camel_imap_command (store, folder, ex, "EXPUNGE"); + if (response) + camel_imap_response_free (store, response); + + /* And fix the remaining messages if we mangled them */ + if (keep_uidset) { + /* Don't pass ex if it's already been set */ + response = camel_imap_command (store, folder, + camel_exception_is_set (ex) ? NULL : ex, + "UID STORE %s +FLAGS.SILENT \\Deleted", + keep_uidset); + g_free (keep_uidset); + if (response) + camel_imap_response_free (store, response); + } + + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); +} + static const char * imap_get_full_name (CamelFolder *folder) { @@ -662,18 +843,39 @@ imap_get_full_name (CamelFolder *folder) return name; } -static char * +static void imap_append_offline (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex) { - /* Fail */ - camel_disco_store_check_online (CAMEL_DISCO_STORE (folder->parent_store), ex); - return NULL; + CamelImapStore *imap_store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapMessageCache *cache = CAMEL_IMAP_FOLDER (folder)->cache; + CamelFolderChangeInfo *changes; + char *uid; + + /* We could keep a separate counter, but this one works fine. */ + CAMEL_IMAP_STORE_LOCK (imap_store, command_lock); + uid = g_strdup_printf ("append-%d", imap_store->command++); + CAMEL_IMAP_STORE_UNLOCK (imap_store, command_lock); + + camel_imap_summary_add_offline (folder->summary, uid, message, info); + camel_imap_message_cache_insert_wrapper (cache, uid, "", + CAMEL_DATA_WRAPPER (message)); + + changes = camel_folder_change_info_new (); + camel_folder_change_info_add_uid (changes, uid); + camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", + changes); + camel_folder_change_info_free (changes); + + camel_disco_diary_log (CAMEL_DISCO_STORE (imap_store)->diary, + CAMEL_DISCO_DIARY_FOLDER_APPEND, folder, uid); + g_free (uid); } -static char * -imap_append_online (CamelFolder *folder, CamelMimeMessage *message, - const CamelMessageInfo *info, CamelException *ex) +static CamelImapResponse * +do_append (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, char **uid, + CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapResponse *response; @@ -681,7 +883,7 @@ imap_append_online (CamelFolder *folder, CamelMimeMessage *message, CamelMimeFilter *crlf_filter; CamelStreamFilter *streamfilter; GByteArray *ba; - char *flagstr, *result, *uid = NULL; + char *flagstr, *result, *end; /* create flag string param */ if (info && info->flags) @@ -725,39 +927,133 @@ imap_append_online (CamelFolder *folder, CamelMimeMessage *message, g_byte_array_append (ba, "\0", 3); response = camel_imap_command_continuation (store, ex, ba->data); g_byte_array_free (ba, TRUE); - if (!response) - return NULL; 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)); - } else { - g_free (uid); - uid = NULL; + *uid = strstrcase (response->status, "[APPENDUID "); + if (*uid) + *uid = strchr (*uid + 11, ' '); + if (*uid) { + *uid = g_strndup (*uid + 1, strcspn (*uid + 1, "]")); + /* Make sure it's a number */ + if (strtoul (*uid, &end, 10) == 0 || *end) { + g_free (*uid); + *uid = NULL; } } + } else + *uid = NULL; + + return response; +} + +static void +imap_append_online (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; + char *uid; + + response = do_append (folder, message, info, &uid, ex); + if (!response) + return; + + if (uid) { + /* Cache first, since freeing response may trigger a + * summary update that will want this information. + */ + camel_imap_message_cache_insert_wrapper ( + CAMEL_IMAP_FOLDER (folder)->cache, + uid, "", CAMEL_DATA_WRAPPER (message)); + g_free (uid); + } + + camel_imap_response_free (store, response); +} + +static void +imap_append_resyncing (CamelFolder *folder, CamelMimeMessage *message, + const CamelMessageInfo *info, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; + char *uid; + + response = do_append (folder, message, info, &uid, ex); + if (!response) + return; + + if (uid) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + const char *olduid = camel_message_info_uid (info); + + CAMEL_IMAP_FOLDER_LOCK (imap_folder, cache_lock); + camel_imap_message_cache_copy (imap_folder->cache, olduid, + imap_folder->cache, uid); + CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); + + camel_disco_diary_uidmap_add (CAMEL_DISCO_STORE (store)->diary, + olduid, uid); } camel_imap_response_free (store, response); - return uid; } + static void imap_copy_offline (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex) { - /* Fail */ - camel_disco_store_check_online (CAMEL_DISCO_STORE (source->parent_store), ex); + CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); + CamelImapMessageCache *sc = CAMEL_IMAP_FOLDER (source)->cache; + CamelImapMessageCache *dc = CAMEL_IMAP_FOLDER (destination)->cache; + CamelFolderChangeInfo *changes; + CamelMimeMessage *message; + CamelMessageInfo *mi; + char *uid, *destuid; + int i; + + /* We grab the store's command lock first, and then grab the + * source and destination cache_locks. This way we can't + * deadlock in the case where we're simultaneously also trying + * to copy messages in the other direction from another thread. + */ + CAMEL_IMAP_STORE_LOCK (store, command_lock); + CAMEL_IMAP_FOLDER_LOCK (source, cache_lock); + CAMEL_IMAP_FOLDER_LOCK (destination, cache_lock); + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + + changes = camel_folder_change_info_new (); + for (i = 0; i < uids->len; i++) { + uid = uids->pdata[i]; + + message = camel_folder_get_message (source, uid, NULL); + if (!message) + continue; + mi = camel_folder_summary_uid (source->summary, uid); + g_return_if_fail (mi != NULL); + + destuid = g_strdup_printf ("copy-%s:%s", source->full_name, uid); + camel_imap_summary_add_offline (destination->summary, destuid, message, mi); + + camel_imap_message_cache_copy (sc, uid, dc, destuid); + camel_folder_summary_info_free (source->summary, mi); + camel_object_unref (CAMEL_OBJECT (message)); + + camel_folder_change_info_add_uid (changes, destuid); + g_free (destuid); + } + + CAMEL_IMAP_FOLDER_UNLOCK (destination, cache_lock); + CAMEL_IMAP_FOLDER_UNLOCK (source, cache_lock); + + camel_object_trigger_event (CAMEL_OBJECT (destination), + "folder_changed", changes); + camel_folder_change_info_free (changes); + + camel_disco_diary_log (CAMEL_DISCO_STORE (store)->diary, + CAMEL_DISCO_DIARY_FOLDER_COPY, + source, destination, uids); } static void @@ -814,19 +1110,13 @@ handle_copyuid (CamelImapResponse *response, CamelFolder *source, } static void -imap_copy_online (CamelFolder *source, GPtrArray *uids, - CamelFolder *destination, CamelException *ex) +do_copy (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); CamelImapResponse *response; char *set; - - /* Sync message flags if needed. */ - imap_sync_online (source, ex); - if (camel_exception_is_set (ex)) - return; - - /* Now copy the messages */ + set = imap_uid_array_to_set (source->summary, uids); response = camel_imap_command (store, source, ex, "UID COPY %s %S", set, destination->full_name); @@ -835,16 +1125,95 @@ imap_copy_online (CamelFolder *source, GPtrArray *uids, camel_imap_response_free (store, response); g_free (set); - +} + +static void +imap_copy_online (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); + CamelImapResponse *response; + + /* Sync message flags if needed. */ + imap_sync_online (source, ex); if (camel_exception_is_set (ex)) return; - + + /* Now copy the messages */ + do_copy (source, uids, destination, ex); + if (camel_exception_is_set (ex)) + return; + /* Force the destination folder to notice its new messages. */ response = camel_imap_command (store, destination, NULL, "NOOP"); camel_imap_response_free (store, response); } static void +imap_copy_resyncing (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex) +{ + CamelDiscoDiary *diary = CAMEL_DISCO_STORE (source->parent_store)->diary; + GPtrArray *realuids; + int first, i; + const char *uid; + CamelMimeMessage *message; + CamelMessageInfo *info; + + /* This is trickier than append_resyncing, because some of + * the messages we are copying may have been copied or + * appended into @source while we were offline, in which case + * if we don't have UIDPLUS, we won't know their real UIDs, + * so we'll have to append them rather than copying. + */ + + realuids = g_ptr_array_new (); + + i = 0; + while (i < uids->len) { + /* Skip past real UIDs */ + for (first = i; i < uids->len; i++) { + uid = uids->pdata[i]; + + if (!isdigit ((unsigned char)*uid)) { + uid = camel_disco_diary_uidmap_lookup (diary, uid); + if (!uid) + break; + } + g_ptr_array_add (realuids, (char *)uid); + } + + /* If we saw any real UIDs, do a COPY */ + if (i != first) { + do_copy (source, realuids, destination, ex); + g_ptr_array_set_size (realuids, 0); + if (i == uids->len || camel_exception_is_set (ex)) + break; + } + + /* Deal with fake UIDs */ + while (i < uids->len && + !isdigit (*(unsigned char *)(uids->pdata[i])) && + !camel_exception_is_set (ex)) { + message = camel_folder_get_message (source, uids->pdata[i], NULL); + if (!message) { + /* Message must have been expunged */ + continue; + } + info = camel_folder_get_message_info (source, uids->pdata[i]); + g_return_if_fail (info != NULL); + + imap_append_online (destination, message, info, ex); + camel_folder_free_message_info (source, info); + camel_object_unref (CAMEL_OBJECT (message)); + i++; + } + } + + g_ptr_array_free (realuids, FALSE); +} + +static void imap_move_messages_to (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex) { @@ -1288,10 +1657,12 @@ camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid, CAMEL_IMAP_FOLDER_LOCK (imap_folder, cache_lock); stream = camel_imap_message_cache_get (imap_folder->cache, uid, section_text); + if (!stream && (!strcmp (section_text, "HEADER") || !strcmp (section_text, "0"))) + stream = camel_imap_message_cache_get (imap_folder->cache, uid, ""); if (stream || cache_only) { CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); return stream; - } + } if (camel_disco_store_status (CAMEL_DISCO_STORE (store)) == CAMEL_DISCO_STORE_OFFLINE) { camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, diff --git a/camel/providers/imap/camel-imap-message-cache.c b/camel/providers/imap/camel-imap-message-cache.c index 45f8d6db51..4b9b97789f 100644 --- a/camel/providers/imap/camel-imap-message-cache.c +++ b/camel/providers/imap/camel-imap-message-cache.c @@ -245,7 +245,6 @@ insert_finish (CamelImapMessageCache *cache, const char *uid, { camel_stream_reset (stream); cache_put (cache, uid, key, stream); - printf ("caching %s\n", path); g_free (path); return stream; @@ -362,10 +361,8 @@ camel_imap_message_cache_get (CamelImapMessageCache *cache, const char *uid, } stream = camel_stream_fs_new_with_name (path, O_RDONLY, 0); - if (stream) { - printf ("got %s\n", path); + if (stream) cache_put (cache, uid, key, stream); - } g_free (path); return stream; diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 94d2436810..d16a7e4a4c 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -40,6 +40,7 @@ #include "camel-imap-folder.h" #include "camel-imap-utils.h" #include "camel-imap-command.h" +#include "camel-disco-diary.h" #include "camel-file-utils.h" #include "camel-folder.h" #include "camel-exception.h" @@ -58,6 +59,7 @@ #define IMAP_PORT 143 static CamelDiscoStoreClass *disco_store_class = NULL; +static CamelRemoteStoreClass *remote_store_class = NULL; static char imap_tag_prefix = 'A'; static void construct (CamelService *service, CamelSession *session, @@ -103,6 +105,7 @@ camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) CAMEL_DISCO_STORE_CLASS (camel_imap_store_class); disco_store_class = CAMEL_DISCO_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ())); + remote_store_class = CAMEL_REMOTE_STORE_CLASS (camel_type_get_global_classfuncs (camel_remote_store_get_type ())); /* virtual method overload */ camel_service_class->construct = construct; @@ -125,8 +128,10 @@ camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) camel_disco_store_class->disconnect_offline = imap_disconnect_offline; camel_disco_store_class->get_folder_online = get_folder_online; camel_disco_store_class->get_folder_offline = get_folder_offline; + camel_disco_store_class->get_folder_resyncing = get_folder_online; camel_disco_store_class->get_folder_info_online = get_folder_info_online; camel_disco_store_class->get_folder_info_offline = get_folder_info_offline; + camel_disco_store_class->get_folder_info_resyncing = get_folder_info_online; } static gboolean @@ -211,7 +216,7 @@ construct (CamelService *service, CamelSession *session, return; imap_store->storage_path = camel_session_get_storage_path (session, service, ex); - if (camel_exception_is_set (ex)) + if (!imap_store->storage_path) return; /* FIXME */ @@ -322,8 +327,11 @@ query_auth_types (CamelService *service, CamelException *ex) GList *types, *sasl_types, *t, *next; gboolean connected; + if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex)) + return NULL; + CAMEL_IMAP_STORE_LOCK (store, command_lock); - connected = connect_to_server (service, ex); + connected = CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex) && connect_to_server (service, ex); CAMEL_IMAP_STORE_UNLOCK (store, command_lock); if (!connected) return NULL; @@ -558,12 +566,12 @@ static gboolean imap_connect_online (CamelService *service, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (service); + CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service); CamelImapResponse *response; int i, flags, len; char *result, *name, *path; FILE *storeinfo; - CAMEL_IMAP_STORE_LOCK (store, command_lock); if (!connect_to_server (service, ex) || !imap_auth_loop (service, ex)) { @@ -668,6 +676,10 @@ imap_connect_online (CamelService *service, CamelException *ex) camel_imap_response_free (store, response); } + path = g_strdup_printf ("%s/journal", store->storage_path); + disco_store->diary = camel_disco_diary_new (disco_store, path, ex); + g_free (path); + done: fclose (storeinfo); CAMEL_IMAP_STORE_UNLOCK (store, command_lock); @@ -683,10 +695,17 @@ static gboolean imap_connect_offline (CamelService *service, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (service); + CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service); char *buf, *name, *path; FILE *storeinfo; guint32 tmp; + path = g_strdup_printf ("%s/journal", store->storage_path); + disco_store->diary = camel_disco_diary_new (disco_store, path, ex); + g_free (path); + if (!disco_store->diary) + return FALSE; + path = g_strdup_printf ("%s/storeinfo", store->storage_path); storeinfo = fopen (path, "r"); g_free (path); @@ -732,6 +751,7 @@ static gboolean imap_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (service); + CamelDiscoStore *disco = CAMEL_DISCO_STORE (service); store->connected = FALSE; if (store->current_folder) { @@ -758,6 +778,11 @@ imap_disconnect_offline (CamelService *service, gboolean clean, CamelException * store->namespace = NULL; } + if (disco->diary) { + camel_object_unref (CAMEL_OBJECT (disco->diary)); + disco->diary = NULL; + } + return TRUE; } diff --git a/camel/providers/imap/camel-imap-summary.c b/camel/providers/imap/camel-imap-summary.c index cd7444559e..e662f013c9 100644 --- a/camel/providers/imap/camel-imap-summary.c +++ b/camel/providers/imap/camel-imap-summary.c @@ -197,3 +197,33 @@ content_info_save (CamelFolderSummary *s, FILE *out, } else return fputc (0, out); } + +void +camel_imap_summary_add_offline (CamelFolderSummary *summary, const char *uid, + CamelMimeMessage *message, + const CamelMessageInfo *info) +{ + CamelMessageInfo *mi; + CamelFlag *flag; + CamelTag *tag; + + /* Create summary entry */ + mi = camel_folder_summary_info_new_from_message (summary, message); + + /* Copy flags 'n' tags */ + mi->flags = info->flags; + flag = info->user_flags; + while (flag) { + camel_flag_set (&mi->user_flags, flag->name, TRUE); + flag = flag->next; + } + tag = info->user_tags; + while (tag) { + camel_tag_set (&mi->user_tags, tag->name, tag->value); + tag = tag->next; + } + + /* Set uid and add to summary */ + camel_message_info_set_uid (mi, g_strdup (uid)); + camel_folder_summary_add (summary, mi); +} diff --git a/camel/providers/imap/camel-imap-summary.h b/camel/providers/imap/camel-imap-summary.h index e3e51c5b7d..e507e75a23 100644 --- a/camel/providers/imap/camel-imap-summary.h +++ b/camel/providers/imap/camel-imap-summary.h @@ -65,5 +65,10 @@ struct _CamelImapSummaryClass { guint camel_imap_summary_get_type (void); CamelFolderSummary *camel_imap_summary_new (const char *filename); +void camel_imap_summary_add_offline (CamelFolderSummary *summary, + const char *uid, + CamelMimeMessage *message, + const CamelMessageInfo *info); + #endif /* ! _CAMEL_IMAP_SUMMARY_H */ diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c index 0d61373b6e..3f581b0c17 100644 --- a/camel/providers/imap/camel-imap-utils.c +++ b/camel/providers/imap/camel-imap-utils.c @@ -585,8 +585,11 @@ get_summary_uid_numeric (CamelFolderSummary *summary, int index) * @summary: summary for the folder the UIDs come from * @uids: a (sorted) array of UIDs * - * Return value: an IMAP "set" covering the listed UIDs, which the - * caller must free with g_free(). + * Creates an IMAP "set" covering the listed UIDs and not covering + * any UIDs that are in @summary but not in @uids. It doesn't + * actually require that all (or any) of the UIDs be in @summary. + * + * Return value: the set, which the caller must free with g_free() **/ char * imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids) @@ -597,6 +600,8 @@ imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids) GString *gset; char *set; + g_return_val_if_fail (uids->len > 0, NULL); + gset = g_string_new (uids->pdata[0]); last_uid = strtoul (uids->pdata[0], NULL, 10); next_summary_uid = 0; @@ -613,7 +618,7 @@ imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids) /* Now get the next UID from @uids */ this_uid = strtoul (uids->pdata[ui], NULL, 10); - if (this_uid == next_summary_uid) + if (this_uid == next_summary_uid || this_uid == last_uid + 1) range = TRUE; else { if (range) { |