From fbb7448b5e10f89471432478789605cfe36ccff3 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 20 Mar 2001 17:38:46 +0000 Subject: Function to check if the store is online and set an exception if not. * providers/imap/camel-imap-store.c (camel_imap_store_check_online): Function to check if the store is online and set an exception if not. Currently controlled by an environment variable, but eventually there will be both a global (session-level) setting and a per-store setting. (construct): Set up storage_path and base_url here rather than at connect-time. (imap_auth_loop): Split out from imap_connect. (imap_setup_online): Split out from imap_connect. Do the post-authentication connection setup, and cache the results to disk. (imap_setup_offline): Set up a CamelImapStore with information saved from a previous imap_setup_online. (imap_connect): If online, do connect_to_server(), imap_auth_loop(), and imap_setup_online(). Otherwise, do imap_setup_offline(). (get_folder, get_folder_info): Add offline support. (create_folder, subscribe_folder, unsubscribe_folder): Disable these when offline (for now). * providers/imap/camel-imap-folder.c (camel_imap_folder_new): Remove the sync'ing-with-server stuff... it's done by camel_imap_folder_selected now, which only gets called if the store is online. (camel_imap_folder_selected): add the code removed from camel_imap_folder_new. Besides simplifying the folder_new and summary_new code, this also means now that we'll DTRT if a folder's UIDVALIDITY changes while we're connected. Also, when that happens, clear the message cache explicitly. (imap_refresh_info, imap_sync): These are no-ops when offline. (imap_expunge, imap_append_message, imap_copy_message_to, imap_search_by_expression): These don't yet work offline. (imap_get_message, camel_imap_folder_fetch_data): Return an error when trying to fetch a non-cached body part when we're offline. * providers/imap/camel-imap-summary.c (camel_imap_summary_new): Rewrite to not check the validity here. (We'll do it from camel_imap_folder_selected instead.) * providers/imap/camel-imap-command.c (camel_imap_command): Call camel_imap_folder_selected even when the selection is all we're doing, to match the changes in camel-imap-folder.c. * providers/imap/camel-imap-message-cache.c (camel_imap_message_cache_clear): New function to clear out a message cache. svn path=/trunk/; revision=8851 --- camel/ChangeLog | 49 +++ camel/providers/imap/camel-imap-command.c | 2 +- camel/providers/imap/camel-imap-folder.c | 115 ++++--- camel/providers/imap/camel-imap-message-cache.c | 13 + camel/providers/imap/camel-imap-message-cache.h | 1 + camel/providers/imap/camel-imap-store.c | 435 +++++++++++++++++------- camel/providers/imap/camel-imap-store.h | 2 + camel/providers/imap/camel-imap-summary.c | 21 +- camel/providers/imap/camel-imap-summary.h | 3 +- 9 files changed, 451 insertions(+), 190 deletions(-) diff --git a/camel/ChangeLog b/camel/ChangeLog index 0f1f790072..d41026b50e 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,52 @@ +2001-03-20 Dan Winship + + * providers/imap/camel-imap-store.c + (camel_imap_store_check_online): Function to check if the store is + online and set an exception if not. Currently controlled by an + environment variable, but eventually there will be both a global + (session-level) setting and a per-store setting. + (construct): Set up storage_path and base_url here rather than at + connect-time. + (imap_auth_loop): Split out from imap_connect. + (imap_setup_online): Split out from imap_connect. Do the + post-authentication connection setup, and cache the results to + disk. + (imap_setup_offline): Set up a CamelImapStore with information + saved from a previous imap_setup_online. + (imap_connect): If online, do connect_to_server(), + imap_auth_loop(), and imap_setup_online(). Otherwise, do + imap_setup_offline(). + (get_folder, get_folder_info): Add offline support. + (create_folder, subscribe_folder, unsubscribe_folder): Disable + these when offline (for now). + + * providers/imap/camel-imap-folder.c (camel_imap_folder_new): + Remove the sync'ing-with-server stuff... it's done by + camel_imap_folder_selected now, which only gets called if the + store is online. + (camel_imap_folder_selected): add the code removed from + camel_imap_folder_new. Besides simplifying the folder_new and + summary_new code, this also means now that we'll DTRT if a + folder's UIDVALIDITY changes while we're connected. Also, when + that happens, clear the message cache explicitly. + (imap_refresh_info, imap_sync): These are no-ops when offline. + (imap_expunge, imap_append_message, imap_copy_message_to, + imap_search_by_expression): These don't yet work offline. + (imap_get_message, camel_imap_folder_fetch_data): Return an error + when trying to fetch a non-cached body part when we're offline. + + * providers/imap/camel-imap-summary.c (camel_imap_summary_new): + Rewrite to not check the validity here. (We'll do it from + camel_imap_folder_selected instead.) + + * providers/imap/camel-imap-command.c (camel_imap_command): Call + camel_imap_folder_selected even when the selection is all we're + doing, to match the changes in camel-imap-folder.c. + + * providers/imap/camel-imap-message-cache.c + (camel_imap_message_cache_clear): New function to clear out a + message cache. + 2001-03-19 Christopher James Lahey * Makefile.am (INCLUDES): Added $(EXTRA_GNOME_CFLAGS) diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index 8a713cde8b..7215168dc7 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -94,10 +94,10 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, return NULL; store->current_folder = folder; + camel_imap_folder_selected (folder, response, ex); if (!fmt) return response; - camel_imap_folder_selected (folder, response, ex); camel_imap_response_free (response); } diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index 6d944747c9..5181b19ed8 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -46,6 +46,7 @@ #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" @@ -162,42 +163,12 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, CamelFolder *folder = CAMEL_FOLDER (camel_object_new (camel_imap_folder_get_type ())); CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelImapResponse *response; - char *resp, *summary_file; - guint32 validity = 0; - int i, exists = 0; + char *summary_file; camel_folder_construct (folder, parent, folder_name, short_name); - CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); - response = camel_imap_command (imap_store, folder, ex, NULL); - CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); - - if (!response) { - camel_object_unref ((CamelObject *)folder); - return NULL; - } - - for (i = 0; i < response->untagged->len; i++) { - resp = response->untagged->pdata[i] + 2; - if (!g_strncasecmp (resp, "FLAGS ", 6)) { - resp += 6; - folder->permanent_flags = imap_parse_flag_list (&resp); - } else if (!g_strncasecmp (resp, "OK [PERMANENTFLAGS ", 19)) { - resp += 19; - folder->permanent_flags = imap_parse_flag_list (&resp); - } else if (!g_strncasecmp (resp, "OK [UIDVALIDITY ", 16)) { - validity = strtoul (resp + 16, NULL, 10); - } else if (isdigit ((unsigned char)*resp)) { - unsigned long num = strtoul (resp, &resp, 10); - - if (!g_strncasecmp (resp, " EXISTS", 7)) - exists = num; - } - } - camel_imap_response_free (response); - summary_file = g_strdup_printf ("%s/summary", folder_dir); - folder->summary = camel_imap_summary_new (summary_file, validity); + folder->summary = camel_imap_summary_new (summary_file); g_free (summary_file); if (!folder->summary) { camel_object_unref (CAMEL_OBJECT (folder)); @@ -213,12 +184,16 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, return NULL; } - CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); - imap_rescan (folder, exists, ex); - CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); - if (camel_exception_is_set (ex)) { - camel_object_unref (CAMEL_OBJECT (folder)); - return NULL; + if (camel_imap_store_check_online (imap_store, NULL)) { + CAMEL_IMAP_STORE_LOCK (imap_store, command_lock); + response = camel_imap_command (imap_store, folder, ex, NULL); + CAMEL_IMAP_STORE_UNLOCK (imap_store, command_lock); + + if (!response) { + camel_object_unref (CAMEL_OBJECT (folder)); + return NULL; + } + camel_imap_response_free (response); } return folder; @@ -230,26 +205,44 @@ camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response, CamelException *ex) { CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - unsigned long exists = 0, val, uid; + CamelImapSummary *imap_summary = CAMEL_IMAP_SUMMARY (folder->summary); + unsigned long exists = 0, validity = 0, val, uid; CamelMessageInfo *info; GData *fetch_data; int i, count; char *resp; + count = camel_folder_summary_count (folder->summary); + for (i = 0; i < response->untagged->len; i++) { resp = response->untagged->pdata[i] + 2; + if (!g_strncasecmp (resp, "FLAGS ", 6) && + !folder->permanent_flags) { + resp += 6; + folder->permanent_flags = imap_parse_flag_list (&resp); + } else if (!g_strncasecmp (resp, "OK [PERMANENTFLAGS ", 19)) { + resp += 19; + folder->permanent_flags = imap_parse_flag_list (&resp); + } else if (!g_strncasecmp (resp, "OK [UIDVALIDITY ", 16)) { + validity = strtoul (resp + 16, NULL, 10); + } else if (isdigit ((unsigned char)*resp)) { + unsigned long num = strtoul (resp, &resp, 10); - exists = strtoul (resp, &resp, 10); - if (!g_strncasecmp (resp, " EXISTS", 7)) - break; + if (!g_strncasecmp (resp, " EXISTS", 7)) + exists = num; + } } - if (i == response->untagged->len) { - g_warning ("Server response did not include EXISTS info"); + + if (!imap_summary->validity) + imap_summary->validity = validity; + else if (validity != imap_summary->validity) { + imap_summary->validity = validity; + camel_folder_summary_clear (folder->summary); + camel_imap_message_cache_clear (imap_folder->cache); + camel_imap_folder_changed (folder, exists, NULL, ex); return; } - count = camel_folder_summary_count (folder->summary); - /* If we've lost messages, we have to rescan everything */ if (exists < count) { imap_rescan (folder, exists, ex); @@ -323,6 +316,9 @@ imap_finalize (CamelObject *object) static void imap_refresh_info (CamelFolder *folder, CamelException *ex) { + if (!camel_imap_store_check_online (CAMEL_IMAP_STORE (folder->parent_store), NULL)) + return; + CAMEL_IMAP_STORE_LOCK (folder->parent_store, command_lock); imap_rescan (folder, camel_folder_summary_count (folder->summary), ex); CAMEL_IMAP_STORE_UNLOCK (folder->parent_store, command_lock); @@ -505,6 +501,9 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) char *set, *flaglist; int i, j, max; + if (!camel_imap_store_check_online (store, NULL)) + return; + max = camel_folder_summary_count (folder->summary); /* If we're expunging then we don't need to be precise about the @@ -594,6 +593,9 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) static void imap_expunge (CamelFolder *folder, CamelException *ex) { + if (!camel_imap_store_check_online (CAMEL_IMAP_STORE (folder->parent_store), ex)) + return; + imap_sync (folder, TRUE, ex); } @@ -624,6 +626,9 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, GByteArray *ba; char *flagstr, *result; + if (!camel_imap_store_check_online (store, ex)) + return; + /* create flag string param */ if (info && info->flags) flagstr = imap_create_flag_list (info->flags); @@ -683,6 +688,9 @@ imap_copy_message_to (CamelFolder *source, const char *uid, CamelImapResponse *response; CamelMessageInfo *mi; + if (!camel_imap_store_check_online (store, ex)) + return; + mi = camel_folder_summary_uid (source->summary, uid); g_return_if_fail (mi != NULL); @@ -746,6 +754,9 @@ imap_search_by_expression (CamelFolder *folder, const char *expression, CamelExc CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); GPtrArray *matches, *summary; + if (!camel_imap_store_check_online (CAMEL_IMAP_STORE (folder->parent_store), ex)) + return NULL; + /* we could get around this by creating a new search object each time, but i doubt its worth it since any long operation would lock the command channel too */ @@ -930,6 +941,12 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) char *body, *found_uid; int i; + if (!camel_imap_store_check_online (store, NULL)) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("This message is not currently available")); + return NULL; + } + CAMEL_IMAP_STORE_LOCK (store, command_lock); response = camel_imap_command (store, folder, ex, "UID FETCH %s BODY", uid); @@ -1121,6 +1138,12 @@ camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid, return stream; } + if (!camel_imap_store_check_online (store, NULL)) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("This message is not currently available")); + return NULL; + } + CAMEL_IMAP_STORE_LOCK (store, command_lock); if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) { response = camel_imap_command (store, folder, ex, diff --git a/camel/providers/imap/camel-imap-message-cache.c b/camel/providers/imap/camel-imap-message-cache.c index 0ac90f7062..a45c039242 100644 --- a/camel/providers/imap/camel-imap-message-cache.c +++ b/camel/providers/imap/camel-imap-message-cache.c @@ -275,3 +275,16 @@ camel_imap_message_cache_remove (CamelImapMessageCache *cache, const char *uid) g_ptr_array_free (subparts, TRUE); } +static gboolean +clear_part (gpointer key, gpointer value, gpointer data) +{ + if (!strchr (key, '.')) + camel_imap_message_cache_remove (data, key); + return TRUE; +} + +void +camel_imap_message_cache_clear (CamelImapMessageCache *cache) +{ + g_hash_table_foreach_remove (cache->parts, clear_part, cache); +} diff --git a/camel/providers/imap/camel-imap-message-cache.h b/camel/providers/imap/camel-imap-message-cache.h index 1ac48086ae..66e3adbaaa 100644 --- a/camel/providers/imap/camel-imap-message-cache.h +++ b/camel/providers/imap/camel-imap-message-cache.h @@ -74,6 +74,7 @@ CamelStream *camel_imap_message_cache_get (CamelImapMessageCache *cache, void camel_imap_message_cache_remove (CamelImapMessageCache *cache, const char *uid); +void camel_imap_message_cache_clear (CamelImapMessageCache *cache); /* Standard Camel function */ CamelType camel_imap_message_cache_get_type (void); diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 44ebea5ebe..21eb673945 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -58,6 +58,9 @@ static CamelRemoteStoreClass *remote_store_class = NULL; +static void construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex); static gboolean imap_connect (CamelService *service, CamelException *ex); static gboolean imap_disconnect (CamelService *service, gboolean clean, CamelException *ex); static GList *query_auth_types (CamelService *service, gboolean connect, CamelException *ex); @@ -76,6 +79,12 @@ static void unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex); static void imap_keepalive (CamelRemoteStore *store); +static gboolean imap_store_setup_online (CamelImapStore *store, + CamelException *ex); +static gboolean imap_store_setup_offline (CamelImapStore *store, + CamelException *ex); + + static void camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) { @@ -91,6 +100,7 @@ camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) (camel_remote_store_get_type ())); /* virtual method overload */ + camel_service_class->construct = construct; camel_service_class->query_auth_types = query_auth_types; camel_service_class->connect = imap_connect; camel_service_class->disconnect = imap_disconnect; @@ -182,6 +192,40 @@ camel_imap_store_get_type (void) return camel_imap_store_type; } +static void +construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (service); + int len; + + CAMEL_SERVICE_CLASS (remote_store_class)->construct (service, session, provider, url, ex); + if (camel_exception_is_set (ex)) + return; + + if (!g_strcasecmp (service->url->protocol, "simap")) { + CamelRemoteStore *rstore = CAMEL_REMOTE_STORE (service); + + rstore->default_port = 993; + rstore->use_ssl = TRUE; + } + + store->storage_path = camel_session_get_storage_path (session, service, ex); + if (camel_exception_is_set (ex)) + return; + + store->base_url = camel_url_to_string (service->url, FALSE); + len = strlen (store->base_url); + if (service->url->path) + store->base_url[len - strlen (service->url->path) + 1] = '\0'; + else { + store->base_url = g_realloc (store->base_url, len + 2); + store->base_url[len] = '/'; + store->base_url[len + 1] = '\0'; + } +} + static struct { const char *name; guint32 flag; @@ -223,13 +267,12 @@ connect_to_server (CamelService *service, CamelException *ex) response = camel_imap_command (store, NULL, ex, "CAPABILITY"); if (!response) return FALSE; - result = camel_imap_response_extract (response, "CAPABILITY", ex); + result = camel_imap_response_extract (response, "CAPABILITY ", ex); if (!result) return FALSE; - /* Skip over "* CAPABILITY". */ - capa = imap_next_word (result + 2); - + /* Skip over "* CAPABILITY ". */ + capa = result + 13; for (capa = strtok_r (capa, " ", &lasts); capa; capa = strtok_r (NULL, " ", &lasts)) { if (!strncmp (capa, "AUTH=", 5)) { @@ -390,26 +433,15 @@ try_auth (CamelImapStore *store, const char *mech, CamelException *ex) } static gboolean -imap_connect (CamelService *service, CamelException *ex) +imap_auth_loop (CamelService *service, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (service); CamelSession *session = camel_service_get_session (service); CamelServiceAuthType *authtype = NULL; - char *result, *errbuf = NULL, *name; - gboolean authenticated = FALSE; CamelImapResponse *response; - int len, i, flags; - - if (!g_strcasecmp (service->url->protocol, "simap")) { - CamelRemoteStore *rstore = CAMEL_REMOTE_STORE (service); - - rstore->default_port = 993; - rstore->use_ssl = TRUE; - } - - if (connect_to_server (service, ex) == 0) - return FALSE; - + char *errbuf = NULL; + gboolean authenticated = FALSE; + if (service->url->authmech) { if (!g_hash_table_lookup (store->authtypes, service->url->authmech)) { camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, @@ -417,7 +449,6 @@ imap_connect (CamelService *service, CamelException *ex) "authentication type %s"), service->url->host, service->url->authmech); - camel_service_disconnect (service, TRUE, NULL); return FALSE; } @@ -426,16 +457,13 @@ imap_connect (CamelService *service, CamelException *ex) camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, _("No support for authentication type %s"), service->url->authmech); - camel_service_disconnect (service, TRUE, NULL); return FALSE; } if (!authtype->need_password) { authenticated = try_auth (store, authtype->authproto, ex); - if (!authenticated) { - camel_service_disconnect (service, TRUE, NULL); + if (!authenticated) return FALSE; - } } } @@ -468,7 +496,6 @@ imap_connect (CamelService *service, CamelException *ex) if (!service->url->passwd) { camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("You didn't enter a password.")); - camel_service_disconnect (service, TRUE, NULL); return FALSE; } } @@ -484,12 +511,12 @@ imap_connect (CamelService *service, CamelException *ex) if (authtype) authenticated = try_auth (store, authtype->authproto, ex); else { - CAMEL_IMAP_STORE_LOCK(store, command_lock); + CAMEL_IMAP_STORE_LOCK (store, command_lock); response = camel_imap_command (store, NULL, ex, "LOGIN %S %S", service->url->user, service->url->passwd); - CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); if (response) { camel_imap_response_free (response); authenticated = TRUE; @@ -503,29 +530,55 @@ imap_connect (CamelService *service, CamelException *ex) } } - /* Get subscribed folders */ - CAMEL_IMAP_STORE_LOCK (store, command_lock); - response = camel_imap_command (store, NULL, ex, "LSUB \"\" \"*\""); - CAMEL_IMAP_STORE_UNLOCK (store, command_lock); - if (!response) - return FALSE; - store->subscribed_folders = g_hash_table_new (g_str_hash, g_str_equal); - for (i = 0; i < response->untagged->len; i++) { - result = response->untagged->pdata[i]; - if (!imap_parse_list_response (result, &flags, NULL, &name)) - continue; - if (flags & (IMAP_LIST_FLAG_MARKED | IMAP_LIST_FLAG_UNMARKED)) - store->useful_lsub = TRUE; - if (flags & IMAP_LIST_FLAG_NOSELECT) { - g_free (name); - continue; + return TRUE; +} + +static gboolean +imap_connect (CamelService *service, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (service); + + if (camel_imap_store_check_online (store, NULL)) { + if (!connect_to_server (service, ex)) + return FALSE; + if (!imap_auth_loop (service, ex)) { + camel_service_disconnect (service, TRUE, NULL); + return FALSE; } - g_hash_table_insert (store->subscribed_folders, name, - GINT_TO_POINTER (1)); - } - camel_imap_response_free (response); + if (!imap_store_setup_online (store, ex)) { + camel_service_disconnect (service, TRUE, NULL); + return FALSE; + } + } else + imap_store_setup_offline (store, ex); + + imap_store_refresh_folders (CAMEL_REMOTE_STORE (store), ex); + return !camel_exception_is_set (ex); +} + +#define IMAP_STOREINFO_VERSION 1 + +static gboolean +imap_store_setup_online (CamelImapStore *store, CamelException *ex) +{ + CamelService *service; + CamelImapResponse *response; + int i, flags, len; + char *result, *name, *path; + FILE *storeinfo; + + path = g_strdup_printf ("%s/storeinfo", store->storage_path); + storeinfo = fopen (path, "w"); + if (!storeinfo) + g_warning ("Could not open storeinfo %s", path); + g_free (path); + + /* Write header and capabilities */ + camel_folder_summary_encode_uint32 (storeinfo, IMAP_STOREINFO_VERSION); + camel_folder_summary_encode_uint32 (storeinfo, store->capabilities); /* Get namespace and hierarchy separator */ + service = CAMEL_SERVICE (store); if (service->url->path && strlen (service->url->path) > 1) store->namespace = g_strdup (service->url->path + 1); else if (store->capabilities & IMAP_CAPABILITY_NAMESPACE) { @@ -559,7 +612,7 @@ imap_connect (CamelService *service, CamelException *ex) store->namespace = g_strdup (""); if (!store->dir_sep) { - CAMEL_IMAP_STORE_LOCK(store, command_lock); + CAMEL_IMAP_STORE_LOCK (store, command_lock); if (store->server_level >= IMAP_LEVEL_IMAP4REV1) { /* This idiom means "tell me the hierarchy separator * for the given path, even if that path doesn't exist. @@ -590,30 +643,78 @@ imap_connect (CamelService *service, CamelException *ex) store->dir_sep = '/'; /* Guess */ } - /* Generate base URL */ - store->base_url = camel_url_to_string (service->url, FALSE); - len = strlen (store->base_url); - if (service->url->path) - store->base_url[len - strlen (service->url->path) + 1] = '\0'; - else { - store->base_url = g_realloc (store->base_url, len + 2); - store->base_url[len] = '/'; - store->base_url[len + 1] = '\0'; + /* Write namespace/separator out */ + camel_folder_summary_encode_string (storeinfo, store->namespace); + camel_folder_summary_encode_uint32 (storeinfo, store->dir_sep); + + /* Get subscribed folders */ + CAMEL_IMAP_STORE_LOCK (store, command_lock); + response = camel_imap_command (store, NULL, ex, "LSUB \"\" \"*\""); + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + if (!response) + return FALSE; + store->subscribed_folders = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < response->untagged->len; i++) { + result = response->untagged->pdata[i]; + if (!imap_parse_list_response (result, &flags, NULL, &name)) + continue; + if (flags & (IMAP_LIST_FLAG_MARKED | IMAP_LIST_FLAG_UNMARKED)) + store->useful_lsub = TRUE; + if (flags & IMAP_LIST_FLAG_NOSELECT) { + g_free (name); + continue; + } + g_hash_table_insert (store->subscribed_folders, name, + GINT_TO_POINTER (1)); + camel_folder_summary_encode_string (storeinfo, result); } + camel_imap_response_free (response); + fclose (storeinfo); - /* Find our storage path. */ - if (!store->storage_path) { - store->storage_path = - camel_session_get_storage_path (session, service, ex); - if (camel_exception_is_set (ex)) - return FALSE; + return TRUE; +} + +static gboolean +imap_store_setup_offline (CamelImapStore *store, CamelException *ex) +{ + char *buf, *name, *path; + FILE *storeinfo; + guint32 tmp; + + path = g_strdup_printf ("%s/storeinfo", store->storage_path); + storeinfo = fopen (path, "r"); + g_free (path); + tmp = 0; + if (storeinfo) + camel_folder_summary_decode_uint32 (storeinfo, &tmp); + if (tmp != IMAP_STOREINFO_VERSION) { + /* This must set ex and return FALSE if we're here... */ + return camel_imap_store_check_online (store, ex); } - imap_store_refresh_folders (CAMEL_REMOTE_STORE (store), ex); + camel_folder_summary_decode_uint32 (storeinfo, &store->capabilities); + camel_folder_summary_decode_string (storeinfo, &store->namespace); + camel_folder_summary_decode_uint32 (storeinfo, &tmp); + store->dir_sep = tmp; - return !camel_exception_is_set (ex); + /* Get subscribed folders */ + store->subscribed_folders = g_hash_table_new (g_str_hash, g_str_equal); + while (camel_folder_summary_decode_string (storeinfo, &buf) == 0) { + if (!imap_parse_list_response (buf, NULL, NULL, &name)) { + g_free (buf); + continue; + } + g_hash_table_insert (store->subscribed_folders, name, + GINT_TO_POINTER (1)); + g_free (buf); + } + + fclose (storeinfo); + return TRUE; } + + static gboolean imap_disconnect (CamelService *service, gboolean clean, CamelException *ex) { @@ -733,22 +834,30 @@ get_folder (CamelStore *store, const char *folder_name, guint32 flags, char *short_name, *folder_dir; gboolean selectable; - if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) - return NULL; + if (camel_imap_store_check_online (imap_store, NULL)) { + if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) + return NULL; - /* lock around the whole lot to check/create atomically */ - CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); - if (!imap_folder_exists (imap_store, folder_name, - &selectable, &short_name, ex)) { - if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0 - || (!imap_create (imap_store, folder_name, ex)) - || (!imap_folder_exists (imap_store, folder_name, - &selectable, &short_name, ex))) { + /* lock around the whole lot to check/create atomically */ + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); + if (!imap_folder_exists (imap_store, folder_name, + &selectable, &short_name, ex) && + ((flags & CAMEL_STORE_FOLDER_CREATE) == 0 + || (!imap_create (imap_store, folder_name, ex)) + || (!imap_folder_exists (imap_store, folder_name, + &selectable, &short_name, ex)))) { CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); return NULL; } + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); + } else { + selectable = g_hash_table_lookup (imap_store->subscribed_folders, folder_name) != NULL; + short_name = strrchr (folder_name, imap_store->dir_sep); + if (short_name) + short_name = g_strdup (short_name + 1); + else + short_name = g_strdup (folder_name); } - CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); if (!selectable) { camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, @@ -798,6 +907,8 @@ create_folder (CamelStore *store, const char *parent_name, CamelFolderInfo *fi; char *full_name; + if (!camel_imap_store_check_online (imap_store, ex)) + return NULL; if (!parent_name) parent_name = imap_store->namespace; full_name = imap_concat (imap_store, parent_name, folder_name); @@ -902,14 +1013,17 @@ get_subscribed_folders_by_hand (CamelImapStore *imap_store, const char *top, } static void -get_folders (CamelImapStore *imap_store, const char *pattern, - GPtrArray *folders, gboolean lsub, CamelException *ex) +get_folders_online (CamelImapStore *imap_store, const char *pattern, + GPtrArray *folders, gboolean lsub, CamelException *ex) { CamelImapResponse *response; CamelFolderInfo *fi; char *list; int i; + if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (imap_store), ex)) + return; + CAMEL_IMAP_STORE_LOCK (imap_store, command_lock); response = camel_imap_command (imap_store, NULL, ex, "%s \"\" %S", lsub ? "LSUB" : "LIST", @@ -921,35 +1035,91 @@ get_folders (CamelImapStore *imap_store, const char *pattern, for (i = 0; i < response->untagged->len; i++) { list = response->untagged->pdata[i]; fi = parse_list_response_as_folder_info (imap_store, list); - if (!fi) - continue; - g_ptr_array_add (folders, fi); + if (fi) + g_ptr_array_add (folders, fi); } camel_imap_response_free (response); } +static void +get_unread_online (CamelImapStore *imap_store, CamelFolderInfo *fi) +{ + CamelImapResponse *response; + char *status, *p; + + CAMEL_IMAP_STORE_LOCK (imap_store, command_lock); + response = camel_imap_command (imap_store, NULL, NULL, + "STATUS %S (UNSEEN)", fi->full_name); + CAMEL_IMAP_STORE_UNLOCK (imap_store, command_lock); + if (!response) + return; + status = camel_imap_response_extract (response, "STATUS", NULL); + if (!status) + return; + + p = e_strstrcase (status, "UNSEEN"); + if (p) + fi->unread_message_count = strtoul (p + 6, NULL, 10); + g_free (status); +} + +static void +add_folder (gpointer key, gpointer value, gpointer data) +{ + g_ptr_array_add (data, key); +} + +static void +get_folders_offline (CamelImapStore *imap_store, GPtrArray *folders, + CamelException *ex) +{ + CamelFolderInfo *fi; + int i; + + i = folders->len; + g_hash_table_foreach (imap_store->subscribed_folders, + add_folder, folders); + while (i < folders->len) { + fi = g_new0 (CamelFolderInfo, 1); + fi->full_name = g_strdup (folders->pdata[i]); + fi->name = strchr (fi->full_name, imap_store->dir_sep); + if (fi->name) + fi->name = g_strdup (fi->name + 1); + else + fi->name = g_strdup (fi->full_name); + fi->url = g_strdup_printf ("%s%s", imap_store->base_url, + fi->full_name); + fi->unread_message_count = -1; + folders->pdata[i++] = fi; + } +} + +static void +get_unread_offline (CamelImapStore *imap_store, CamelFolderInfo *fi) +{ + /* FIXME */ +} + static CamelFolderInfo * get_folder_info (CamelStore *store, const char *top, gboolean fast, gboolean recursive, gboolean subscribed_only, CamelException *ex) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); - gboolean need_inbox = FALSE; - CamelImapResponse *response; + gboolean need_inbox = FALSE, online; GPtrArray *folders; - const char *name, *p; - char *pattern, *status; + const char *name; + char *pattern; CamelFolderInfo *fi; int i; - if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) - return NULL; + if (!subscribed_only || !recursive || top) { + if (!camel_imap_store_check_online (imap_store, ex)) + return NULL; + } else + online = camel_imap_store_check_online (imap_store, NULL); - /* Sync flag changes to the server so it has the same ideas about - * read/unread as we do. - */ - camel_store_sync (store, ex); - if (camel_exception_is_set (ex)) + if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) return NULL; name = top; @@ -960,22 +1130,30 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, folders = g_ptr_array_new (); - get_folders (imap_store, name, folders, FALSE, ex); - if (camel_exception_is_set (ex)) - return NULL; - if (folders->len) { - fi = folders->pdata[0]; - if (!fi->url) - g_ptr_array_remove_index (folders, 0); - } + if (online) { + /* Get top-level */ + get_folders_online (imap_store, name, folders, FALSE, ex); + if (camel_exception_is_set (ex)) + return NULL; + if (folders->len) { + fi = folders->pdata[0]; + if (!fi->url) + g_ptr_array_remove_index (folders, 0); + } + + if (subscribed_only && !imap_store->useful_lsub) + get_subscribed_folders_by_hand (imap_store, name, + folders, ex); + else { + pattern = imap_concat (imap_store, name, + recursive ? "*" : "%"); + get_folders_online (imap_store, pattern, folders, + subscribed_only, ex); + g_free (pattern); + } + } else + get_folders_offline (imap_store, folders, ex); - if (subscribed_only && !imap_store->useful_lsub) - get_subscribed_folders_by_hand (imap_store, name, folders, ex); - else { - pattern = imap_concat (imap_store, name, recursive ? "*" : "%"); - get_folders (imap_store, pattern, folders, subscribed_only, ex); - g_free (pattern); - } if (camel_exception_is_set (ex)) { for (i = 0; i < folders->len; i++) camel_folder_info_free (folders->pdata[i]); @@ -1004,7 +1182,11 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, } if (!fast) { - /* Get unread counts */ + /* Get unread counts. Sync flag changes to the server + * first so it has the same ideas about read/unread as + * we do. + */ + camel_store_sync (store, NULL); for (i = 0; i < folders->len; i++) { fi = folders->pdata[i]; if (!fi->url || fi->unread_message_count != -1) @@ -1021,21 +1203,10 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, continue; } - CAMEL_IMAP_STORE_LOCK (imap_store, command_lock); - response = camel_imap_command (imap_store, NULL, NULL, - "STATUS %S (UNSEEN)", - fi->full_name); - CAMEL_IMAP_STORE_UNLOCK (imap_store, command_lock); - if (!response) - continue; - status = camel_imap_response_extract (response, "STATUS", NULL); - if (!status) - continue; - - p = e_strstrcase (status, "UNSEEN"); - if (p) - fi->unread_message_count = strtoul (p + 6, NULL, 10); - g_free (status); + if (online) + get_unread_online (imap_store, fi); + else + get_unread_offline (imap_store, fi); } } @@ -1063,6 +1234,8 @@ subscribe_folder (CamelStore *store, const char *folder_name, CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; + if (!camel_imap_store_check_online (imap_store, ex)) + return; if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) return; @@ -1104,6 +1277,8 @@ unsubscribe_folder (CamelStore *store, const char *folder_name, CamelImapResponse *response; gpointer key, value; + if (!camel_imap_store_check_online (imap_store, ex)) + return; if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) return; @@ -1150,3 +1325,17 @@ imap_keepalive (CamelRemoteStore *store) CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); camel_imap_response_free (response); } + +gboolean +camel_imap_store_check_online (CamelImapStore *store, CamelException *ex) +{ + /* Hack */ + if (getenv ("CAMEL_OFFLINE")) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("You must be working online to " + "complete this operation")); + return FALSE; + } + + return TRUE; +} diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index 3792e92872..8cc186655c 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -83,6 +83,8 @@ typedef struct { /* Standard Camel function */ CamelType camel_imap_store_get_type (void); +gboolean camel_imap_store_check_online (CamelImapStore *store, CamelException *ex); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/providers/imap/camel-imap-summary.c b/camel/providers/imap/camel-imap-summary.c index 66228649bc..34597cfe31 100644 --- a/camel/providers/imap/camel-imap-summary.c +++ b/camel/providers/imap/camel-imap-summary.c @@ -99,16 +99,14 @@ camel_imap_summary_init (CamelImapSummary *obj) /** * camel_imap_summary_new: * @filename: the file to store the summary in. - * @validity: the current UIDVALIDITY value of the folder * * This will create a new CamelImapSummary object and read in the - * summary data from disk, if it exists and has the right UIDVALIDITY - * value. + * summary data from disk, if it exists. * * Return value: A new CamelImapSummary object. **/ CamelFolderSummary * -camel_imap_summary_new (const char *filename, guint32 validity) +camel_imap_summary_new (const char *filename) { CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_imap_summary_get_type ())); @@ -118,21 +116,8 @@ camel_imap_summary_new (const char *filename, guint32 validity) camel_folder_summary_set_filename (summary, filename); if (camel_folder_summary_load (summary) == -1) { - if (errno == ENOENT) { - imap_summary->validity = validity; - return summary; - } else { - /* FIXME: are there error conditions where this won't work? */ - camel_folder_summary_clear (summary); - camel_folder_summary_touch (summary); - - return summary; - } - } - - if (imap_summary->validity != validity) { camel_folder_summary_clear (summary); - imap_summary->validity = validity; + camel_folder_summary_touch (summary); } return summary; diff --git a/camel/providers/imap/camel-imap-summary.h b/camel/providers/imap/camel-imap-summary.h index 7ad1e3f4e8..e3e51c5b7d 100644 --- a/camel/providers/imap/camel-imap-summary.h +++ b/camel/providers/imap/camel-imap-summary.h @@ -63,8 +63,7 @@ struct _CamelImapSummaryClass { }; guint camel_imap_summary_get_type (void); -CamelFolderSummary *camel_imap_summary_new (const char *filename, - guint32 validity); +CamelFolderSummary *camel_imap_summary_new (const char *filename); #endif /* ! _CAMEL_IMAP_SUMMARY_H */ -- cgit v1.2.3