diff options
author | Dan Winship <danw@src.gnome.org> | 2001-05-30 01:09:12 +0800 |
---|---|---|
committer | Dan Winship <danw@src.gnome.org> | 2001-05-30 01:09:12 +0800 |
commit | 43795a43b5c5b8ccb40cdc3379d168d7208ea5a6 (patch) | |
tree | 9cc08f6fb26ad6f8f8249ea6406fa9eb3bd7476c /camel/providers | |
parent | 11ea26d0e33405490795a766406d5e065b6b9234 (diff) | |
download | gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar.gz gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar.bz2 gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar.lz gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar.xz gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.tar.zst gsoc2013-evolution-43795a43b5c5b8ccb40cdc3379d168d7208ea5a6.zip |
Create a CamelDiscoDiary. (imap_disconnect_offline): And free it.
* providers/imap/camel-imap-store.c (imap_connect_online,
imap_connect_offline): Create a CamelDiscoDiary.
(imap_disconnect_offline): And free it.
* providers/imap/camel-imap-folder.c (camel_imap_folder_selected):
If RESYNCING, don't do any sort of checking that the remote folder
matches the summary, beyond making sure that the UIDVALIDITY is
correct.
(imap_rescan): Add a missing camel_folder_summary_info_free when
removing a UID from the summary.
(imap_expunge_uids_offline): Implement. Fairly simple.
(imap_expunge_uids_resyncing): Implement. If the store supports
UIDPLUS, we can just use imap_expunge_uids_online. If not, we need
to temporarily undelete any messages marked deleted on the server
that aren't supposed to get expunged.
(imap_append_offline): Implement, using cache and summary
operations, and triggering the folder_changed event by hand.
(imap_append_resyncing): Implement. Redo imap_append_online a bit
in the process to make them able to share more code.
(imap_copy_offline): Implement.
(imap_copy_online): Move parts of this out into a helper.
(imap_copy_resyncing): Implement. In most cases this is just like
imap_copy_online, but if you are copying a message that was itself
copied or appended into the folder, and the server doesn't do
UIDPLUS, it will be necessary to replace at least part of the copy
operation with one or more appends.
* providers/imap/camel-imap-command.c (imap_read_response): Don't
record the current folder in the response when in RESYNCING mode.
(This means that EXISTS and EXPUNGE responses won't be processed,
which is needed because the summary may not match the folder at
this point.)
(imap_read_response): On error, call
camel_imap_response_free_without_processing, not
camel_imap_response_free.
* providers/imap/camel-imap-utils.c (imap_uid_array_to_set): Make
this work better when operating on UIDs that aren't in the summary.
* providers/imap/camel-imap-summary.c
(camel_imap_summary_add_offline): New routine used by
imap_append_offline and imap_copy_offline to create new summary
entries.
svn path=/trunk/; revision=10041
Diffstat (limited to 'camel/providers')
-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) { |