diff options
-rw-r--r-- | camel/ChangeLog | 91 | ||||
-rw-r--r-- | camel/camel-private.h | 14 | ||||
-rw-r--r-- | camel/camel-store-summary.c | 288 | ||||
-rw-r--r-- | camel/camel-store-summary.h | 93 | ||||
-rw-r--r-- | camel/camel-store.h | 2 | ||||
-rw-r--r-- | camel/camel-url.h | 2 | ||||
-rw-r--r-- | camel/providers/imap/Makefile.am | 2 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-command.c | 8 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store-summary.c | 519 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store-summary.h | 100 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.c | 815 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.h | 4 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.c | 15 |
13 files changed, 1379 insertions, 574 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index c7183dd317..7e65ed67ef 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,13 @@ +2002-09-04 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-store.c, + providers/imap/camel-imap-folder.c: Lots of changes, too numerous + to list. Changed to use camel-imap-store-summary to cache list + requests. Changed to use a canonicalised url path with / instead + of per-store directory separator. Indirects folder name so + invalid folder names can still be accessed. Summary now stored in + a new expandable format in .ev-store-summary. + 2002-08-29 Not Zed <NotZed@Ximian.com> * tests/folder/test3.c: Change the case sensitive search of @@ -123,6 +133,87 @@ (camel_session_register_timeout): Removed. (camel_session_remove_timeout): Removed. +2002-08-28 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-store.c (construct): Load store + summary if it exists. + (can_work_offline): Just see if we have any folders to say whether + we can work offline or not. Should probably always just return + true. + +2002-08-27 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-store-summary.[ch]: New files to + handle offline definition of folders, etc. + + * camel-url.h: Define CamelURL to be struct _CamelURL rather than + anonymous struct. + + * camel-store-summary.[ch]: a few api tweaks. Also, the summary + header is versioned separately at each level, so that version + upgrades can be handled separately. Renamed FolderInfo -> + StoreInfo to avoid namespace with current FolderInfo code. This + should be reversed when the FolderInfo code is rationalised to + this new base. + +2002-08-23 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-command.c (camel_imap_command): domt + encode folder name. + + * providers/imap/camel-imap-folder.c (do_copy): dont encode folder + name. + (do_append): dont encode folder name. + + * providers/imap/camel-imap-store.c (get_folder_status): don + encode folder name in imap request. + (get_folder_online): here too for creating folder. + (rename_folder): Assume the incoming 'new name' is a utf8 path, + whereas the 'old name' is as from get folder info (raw). + (create_folder): Dont encode parent_name, assume its the raw + thing. + +2002-08-22 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-store.c (get_folder_online): Select + based on unconverted name. + (imap_build_folder_info): New function to create a folderinfo + properly based on raw name. + (subscribe_folder): Use above helper. + (imap_folder_effectively_unsubscribed): Same here. + (imap_forget_folder): Same here. + (get_one_folder_offline): " + +2002-08-21 Not Zed <NotZed@Ximian.com> + + * providers/imap/camel-imap-store.c + (parse_list_response_as_folder_info): Setup path properly, as + decoded path with / separator. Setup full_name as non-decoded raw + name. Keep url as decoded path but with server separator + ... (ick). + (create_folder): Dont call build_path anymore, get_folders() does + it for us. + (subscribe_folder): Build the path ourself. + (imap_folder_effectively_unsubscribed): Same here. + (get_subscribed_folders): list using %S not %F, we're using the + raw server provided name directly. + (subscribe_folder): As above, for SUBSCRIBE. + (unsubscribe_folder): Same here. + (delete_folder): Same. + (rename_folder_info): Same here for source name. + (rename_folder): And here? + (get_folders_online): Amd here. + + * providers/imap/camel-imap-utils.c: + (imap_parse_list_response): Dont decode the mailbox. + + * camel-utf8.[ch]: some new utf8 & utf7 utilities. + + * providers/imap/camel-imap-utils.c (imap_mailbox_encode): + (imap_mailbox_decode): use camel_utf7/8* functions instead. + : Add config.h and alloca.h headers. + +>>>>>>> 1.1620.2.1 2002-08-20 Jeffrey Stedfast <fejj@ximian.com> * providers/imap/camel-imap-store.c (imap_noop): Override the diff --git a/camel/camel-private.h b/camel/camel-private.h index e5a7894e6f..5165f31c8b 100644 --- a/camel/camel-private.h +++ b/camel/camel-private.h @@ -61,7 +61,7 @@ struct _CamelStorePrivate { EMutex *folder_lock; /* for locking folder operations */ EMutex *cache_lock; /* for locking access to the cache */ #else - gpointer dummy; + int dummy; #endif }; @@ -77,7 +77,7 @@ struct _CamelTransportPrivate { #ifdef ENABLE_THREADS GMutex *send_lock; /* for locking send operations */ #else - gpointer dummy; + int dummy; #endif }; @@ -94,7 +94,7 @@ struct _CamelServicePrivate { EMutex *connect_lock; /* for locking connection operations */ EMutex *connect_op_lock;/* for locking the connection_op */ #else - gpointer dummy; + int dummy; #endif }; @@ -117,7 +117,7 @@ struct _CamelSessionPrivate { GHashTable *thread_active; EThread *thread_queue; #else - gpointer dummy; + int dummy; #endif }; @@ -169,7 +169,7 @@ struct _CamelStoreSummaryPrivate { GMutex *alloc_lock; /* for setting up and using allocators */ GMutex *ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */ #else - gpointer dummy; + int dummy; #endif }; @@ -205,7 +205,7 @@ struct _CamelDataWrapperPrivate { #ifdef ENABLE_THREADS pthread_mutex_t stream_lock; #else - gpointer dummy; + int dummy; #endif }; @@ -225,7 +225,7 @@ struct _CamelCertDBPrivate { GMutex *alloc_lock; /* for setting up and using allocators */ GMutex *ref_lock; /* for reffing/unreffing certs */ #else - gpointer dummy; + int dummy; #endif }; diff --git a/camel/camel-store-summary.c b/camel/camel-store-summary.c index 8cd2bf37dd..b114ac78f4 100644 --- a/camel/camel-store-summary.c +++ b/camel/camel-store-summary.c @@ -22,6 +22,9 @@ #include <config.h> #endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <unistd.h> #include <ctype.h> #include <string.h> @@ -37,24 +40,29 @@ #include "e-util/e-memory.h" #include "camel-private.h" +#include "camel-url.h" #define d(x) #define io(x) /* io debug */ -#define CAMEL_STORE_SUMMARY_VERSION (13) +/* possible versions, for versioning changes */ +#define CAMEL_STORE_SUMMARY_VERSION_0 (1) + +/* current version */ +#define CAMEL_STORE_SUMMARY_VERSION (1) #define _PRIVATE(o) (((CamelStoreSummary *)(o))->priv) static int summary_header_load(CamelStoreSummary *, FILE *); static int summary_header_save(CamelStoreSummary *, FILE *); -static CamelFolderInfo * folder_info_new(CamelStoreSummary *, const char *); -static CamelFolderInfo * folder_info_load(CamelStoreSummary *, FILE *); -static int folder_info_save(CamelStoreSummary *, FILE *, CamelFolderInfo *); -static void folder_info_free(CamelStoreSummary *, CamelFolderInfo *); +static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *); +static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *); +static int store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *); +static void store_info_free(CamelStoreSummary *, CamelStoreInfo *); -static const char *folder_info_string(CamelStoreSummary *, const CamelFolderInfo *, int); -static void folder_info_set_string(CamelStoreSummary *, CamelFolderInfo *, int, const char *); +static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int); +static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *); static void camel_store_summary_class_init (CamelStoreSummaryClass *klass); static void camel_store_summary_init (CamelStoreSummary *obj); @@ -70,13 +78,13 @@ camel_store_summary_class_init (CamelStoreSummaryClass *klass) klass->summary_header_load = summary_header_load; klass->summary_header_save = summary_header_save; - klass->folder_info_new = folder_info_new; - klass->folder_info_load = folder_info_load; - klass->folder_info_save = folder_info_save; - klass->folder_info_free = folder_info_free; + klass->store_info_new = store_info_new; + klass->store_info_load = store_info_load; + klass->store_info_save = store_info_save; + klass->store_info_free = store_info_free; - klass->folder_info_string = folder_info_string; - klass->folder_info_set_string = folder_info_set_string; + klass->store_info_string = store_info_string; + klass->store_info_set_string = store_info_set_string; } static void @@ -86,9 +94,9 @@ camel_store_summary_init (CamelStoreSummary *s) p = _PRIVATE(s) = g_malloc0(sizeof(*p)); - s->folder_info_size = sizeof(CamelFolderInfo); + s->store_info_size = sizeof(CamelStoreInfo); - s->folder_info_chunks = NULL; + s->store_info_chunks = NULL; s->version = CAMEL_STORE_SUMMARY_VERSION; s->flags = 0; @@ -96,7 +104,7 @@ camel_store_summary_init (CamelStoreSummary *s) s->time = 0; s->folders = g_ptr_array_new(); - s->folders_full = g_hash_table_new(g_str_hash, g_str_equal); + s->folders_path = g_hash_table_new(g_str_hash, g_str_equal); #ifdef ENABLE_THREADS p->summary_lock = g_mutex_new(); @@ -116,12 +124,12 @@ camel_store_summary_finalise (CamelObject *obj) camel_store_summary_clear(s); g_ptr_array_free(s->folders, TRUE); - g_hash_table_destroy(s->folders_full); + g_hash_table_destroy(s->folders_path); g_free(s->summary_path); - if (s->folder_info_chunks) - e_memchunk_destroy(s->folder_info_chunks); + if (s->store_info_chunks) + e_memchunk_destroy(s->store_info_chunks); #ifdef ENABLE_THREADS g_mutex_free(p->summary_lock); @@ -181,12 +189,13 @@ void camel_store_summary_set_filename(CamelStoreSummary *s, const char *name) CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); } -void camel_store_summary_set_uri_prefix(CamelStoreSummary *s, const char *prefix) +void camel_store_summary_set_uri_base(CamelStoreSummary *s, CamelURL *base) { CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); - g_free(s->uri_prefix); - s->uri_prefix = g_strdup(prefix); + if (s->uri_base) + camel_url_free(s->uri_base); + s->uri_base = camel_url_new_with_base(base, ""); CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); } @@ -219,10 +228,10 @@ camel_store_summary_count(CamelStoreSummary *s) * of range. * It must be freed using camel_store_summary_info_free(). **/ -CamelFolderInfo * +CamelStoreInfo * camel_store_summary_index(CamelStoreSummary *s, int i) { - CamelFolderInfo *info = NULL; + CamelStoreInfo *info = NULL; CAMEL_STORE_SUMMARY_LOCK(s, ref_lock); CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); @@ -253,7 +262,7 @@ camel_store_summary_index(CamelStoreSummary *s, int i) GPtrArray * camel_store_summary_array(CamelStoreSummary *s) { - CamelFolderInfo *info; + CamelStoreInfo *info; GPtrArray *res = g_ptr_array_new(); int i; @@ -291,28 +300,28 @@ camel_store_summary_array_free(CamelStoreSummary *s, GPtrArray *array) } /** - * camel_store_summary_full: + * camel_store_summary_path: * @s: - * @full: + * @path: * - * Retrieve a summary item by full name. + * Retrieve a summary item by path name. * * A referenced to the summary item is returned, which may be * ref'd or free'd as appropriate. * - * Return value: The summary item, or NULL if the @full name + * Return value: The summary item, or NULL if the @path name * is not available. * It must be freed using camel_store_summary_info_free(). **/ -CamelFolderInfo * -camel_store_summary_full(CamelStoreSummary *s, const char *full) +CamelStoreInfo * +camel_store_summary_path(CamelStoreSummary *s, const char *path) { - CamelFolderInfo *info; + CamelStoreInfo *info; CAMEL_STORE_SUMMARY_LOCK(s, ref_lock); CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); - info = g_hash_table_lookup(s->folders_full, full); + info = g_hash_table_lookup(s->folders_path, path); CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); @@ -329,7 +338,7 @@ camel_store_summary_load(CamelStoreSummary *s) { FILE *in; int i; - CamelFolderInfo *mi; + CamelStoreInfo *mi; g_assert(s->summary_path); @@ -343,7 +352,7 @@ camel_store_summary_load(CamelStoreSummary *s) /* now read in each message ... */ for (i=0;i<s->count;i++) { - mi = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_load(s, in); + mi = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_load(s, in); if (mi == NULL) goto error; @@ -385,18 +394,25 @@ camel_store_summary_save(CamelStoreSummary *s) int fd; int i; guint32 count; - CamelFolderInfo *mi; + CamelStoreInfo *mi; g_assert(s->summary_path); - if ((s->flags & CAMEL_STORE_SUMMARY_DIRTY) == 0) + io(printf("** saving summary\n")); + + if ((s->flags & CAMEL_STORE_SUMMARY_DIRTY) == 0) { + io(printf("** summary clean no save\n")); return 0; + } - fd = open(s->summary_path, O_RDWR|O_CREAT, 0600); - if (fd == -1) + fd = open(s->summary_path, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd == -1) { + io(printf("** open error: %s\n", strerror(errno))); return -1; + } out = fdopen(fd, "w"); if ( out == NULL ) { + printf("** fdopen error: %s\n", strerror(errno)); close(fd); return -1; } @@ -418,7 +434,7 @@ camel_store_summary_save(CamelStoreSummary *s) count = s->folders->len; for (i=0;i<count;i++) { mi = s->folders->pdata[i]; - ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_save(s, out, mi); + ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_save(s, out, mi); } CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock); @@ -472,27 +488,27 @@ int camel_store_summary_header_load(CamelStoreSummary *s) * info_new_*() functions, as it will be free'd based on the summary * class. And MUST NOT be allocated directly using malloc. **/ -void camel_store_summary_add(CamelStoreSummary *s, CamelFolderInfo *info) +void camel_store_summary_add(CamelStoreSummary *s, CamelStoreInfo *info) { if (info == NULL) return; - if (camel_folder_info_full(s, info) == NULL) { - g_warning("Trying to add a folder info with missing required full name\n"); + if (camel_store_info_path(s, info) == NULL) { + g_warning("Trying to add a folder info with missing required path name\n"); return; } CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); g_ptr_array_add(s->folders, info); - g_hash_table_insert(s->folders_full, (char *)camel_folder_info_full(s, info), info); + g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, info), info); s->flags |= CAMEL_STORE_SUMMARY_DIRTY; CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); } /** - * camel_store_summary_add_from_full: + * camel_store_summary_add_from_path: * @s: * @h: * @@ -500,20 +516,20 @@ void camel_store_summary_add(CamelStoreSummary *s, CamelFolderInfo *info) * * Return value: The newly added record. **/ -CamelFolderInfo *camel_store_summary_add_from_full(CamelStoreSummary *s, const char *full) +CamelStoreInfo *camel_store_summary_add_from_path(CamelStoreSummary *s, const char *path) { - CamelFolderInfo *info; + CamelStoreInfo *info; CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); - info = g_hash_table_lookup(s->folders_full, full); + info = g_hash_table_lookup(s->folders_path, path); if (info != NULL) { - g_warning("Trying to add folder '%s' to summary that already has it", full); + g_warning("Trying to add folder '%s' to summary that already has it", path); info = NULL; } else { - info = camel_store_summary_info_new_from_full(s, full); + info = camel_store_summary_info_new_from_path(s, path); g_ptr_array_add(s->folders, info); - g_hash_table_insert(s->folders_full, (char *)camel_folder_info_full(s, info), info); + g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, info), info); s->flags |= CAMEL_STORE_SUMMARY_DIRTY; } @@ -523,18 +539,18 @@ CamelFolderInfo *camel_store_summary_add_from_full(CamelStoreSummary *s, const c } /** - * camel_store_summary_info_new_from_full: + * camel_store_summary_info_new_from_path: * @s: * @h: * * Create a new info record from a name. * * Return value: Guess? This info record MUST be freed using - * camel_store_summary_info_free(), camel_folder_info_free() will not work. + * camel_store_summary_info_free(), camel_store_info_free() will not work. **/ -CamelFolderInfo *camel_store_summary_info_new_from_full(CamelStoreSummary *s, const char *f) +CamelStoreInfo *camel_store_summary_info_new_from_path(CamelStoreSummary *s, const char *f) { - return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> folder_info_new(s, f); + return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> store_info_new(s, f); } /** @@ -544,7 +560,7 @@ CamelFolderInfo *camel_store_summary_info_new_from_full(CamelStoreSummary *s, co * * Unref and potentially free the message info @mi, and all associated memory. **/ -void camel_store_summary_info_free(CamelStoreSummary *s, CamelFolderInfo *mi) +void camel_store_summary_info_free(CamelStoreSummary *s, CamelStoreInfo *mi) { g_assert(mi); g_assert(s); @@ -561,7 +577,7 @@ void camel_store_summary_info_free(CamelStoreSummary *s, CamelFolderInfo *mi) CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock); - ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_free(s, mi); + ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_free(s, mi); } /** @@ -571,7 +587,7 @@ void camel_store_summary_info_free(CamelStoreSummary *s, CamelFolderInfo *mi) * * Add an extra reference to @mi. **/ -void camel_store_summary_info_ref(CamelStoreSummary *s, CamelFolderInfo *mi) +void camel_store_summary_info_ref(CamelStoreSummary *s, CamelStoreInfo *mi) { g_assert(mi); g_assert(s); @@ -617,8 +633,8 @@ camel_store_summary_clear(CamelStoreSummary *s) camel_store_summary_info_free(s, s->folders->pdata[i]); g_ptr_array_set_size(s->folders, 0); - g_hash_table_destroy(s->folders_full); - s->folders_full = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_destroy(s->folders_path); + s->folders_path = g_hash_table_new(g_str_hash, g_str_equal); s->flags |= CAMEL_STORE_SUMMARY_DIRTY; CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); } @@ -630,10 +646,10 @@ camel_store_summary_clear(CamelStoreSummary *s) * * Remove a specific @info record from the summary. **/ -void camel_store_summary_remove(CamelStoreSummary *s, CamelFolderInfo *info) +void camel_store_summary_remove(CamelStoreSummary *s, CamelStoreInfo *info) { CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); - g_hash_table_remove(s->folders_full, camel_folder_info_full(s, info)); + g_hash_table_remove(s->folders_path, camel_store_info_path(s, info)); g_ptr_array_remove(s->folders, info); s->flags |= CAMEL_STORE_SUMMARY_DIRTY; CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); @@ -644,18 +660,18 @@ void camel_store_summary_remove(CamelStoreSummary *s, CamelFolderInfo *info) /** * camel_store_summary_remove_uid: * @s: - * @full: + * @path: * - * Remove a specific info record from the summary, by @full. + * Remove a specific info record from the summary, by @path. **/ -void camel_store_summary_remove_full(CamelStoreSummary *s, const char *full) +void camel_store_summary_remove_path(CamelStoreSummary *s, const char *path) { - CamelFolderInfo *oldinfo; - char *oldfull; + CamelStoreInfo *oldinfo; + char *oldpath; CAMEL_STORE_SUMMARY_LOCK(s, ref_lock); CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); - if (g_hash_table_lookup_extended(s->folders_full, full, (void *)&oldfull, (void *)&oldinfo)) { + if (g_hash_table_lookup_extended(s->folders_path, path, (void *)&oldpath, (void *)&oldinfo)) { /* make sure it doesn't vanish while we're removing it */ oldinfo->refcount++; CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); @@ -679,9 +695,9 @@ void camel_store_summary_remove_index(CamelStoreSummary *s, int index) { CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); if (index < s->folders->len) { - CamelFolderInfo *info = s->folders->pdata[index]; + CamelStoreInfo *info = s->folders->pdata[index]; - g_hash_table_remove(s->folders_full, camel_folder_info_full(s, info)); + g_hash_table_remove(s->folders_path, camel_store_info_path(s, info)); g_ptr_array_remove_index(s->folders, index); s->flags |= CAMEL_STORE_SUMMARY_DIRTY; @@ -712,10 +728,13 @@ summary_header_load(CamelStoreSummary *s, FILE *in) s->flags = flags; s->time = time; s->count = count; - if (s->version != version) { - g_warning("Summary header version mismatch"); + s->version = version; + + if (version < CAMEL_STORE_SUMMARY_VERSION_0) { + g_warning("Store summary header version too low"); return -1; } + return 0; } @@ -726,7 +745,8 @@ summary_header_save(CamelStoreSummary *s, FILE *out) io(printf("Savining header\n")); - camel_file_util_encode_fixed_int32(out, s->version); + /* always write latest version */ + camel_file_util_encode_fixed_int32(out, CAMEL_STORE_SUMMARY_VERSION); camel_file_util_encode_fixed_int32(out, s->flags); camel_file_util_encode_time_t(out, s->time); return camel_file_util_encode_fixed_int32(out, camel_store_summary_count(s)); @@ -741,54 +761,54 @@ summary_header_save(CamelStoreSummary *s, FILE *out) * * Return value: **/ -CamelFolderInfo * +CamelStoreInfo * camel_store_summary_info_new(CamelStoreSummary *s) { - CamelFolderInfo *mi; + CamelStoreInfo *mi; CAMEL_STORE_SUMMARY_LOCK(s, alloc_lock); - if (s->folder_info_chunks == NULL) - s->folder_info_chunks = e_memchunk_new(32, s->folder_info_size); - mi = e_memchunk_alloc0(s->folder_info_chunks); + if (s->store_info_chunks == NULL) + s->store_info_chunks = e_memchunk_new(32, s->store_info_size); + mi = e_memchunk_alloc0(s->store_info_chunks); CAMEL_STORE_SUMMARY_UNLOCK(s, alloc_lock); mi->refcount = 1; return mi; } -const char *camel_folder_info_string(CamelStoreSummary *s, const CamelFolderInfo *mi, int type) +const char *camel_store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) { - return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_string(s, mi, type); + return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_string(s, mi, type); } -void camel_folder_info_set_string(CamelStoreSummary *s, CamelFolderInfo *mi, int type, const char *value) +void camel_store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *value) { - return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_set_string(s, mi, type, value); + return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->store_info_set_string(s, mi, type, value); } -static CamelFolderInfo * -folder_info_new(CamelStoreSummary *s, const char *f) +static CamelStoreInfo * +store_info_new(CamelStoreSummary *s, const char *f) { - CamelFolderInfo *mi; + CamelStoreInfo *mi; mi = camel_store_summary_info_new(s); - mi->full = g_strdup(f); - mi->unread = CAMEL_STORE_SUMMARY_UNKNOWN; - mi->total = CAMEL_STORE_SUMMARY_UNKNOWN; + mi->path = g_strdup(f); + mi->unread = CAMEL_STORE_INFO_FOLDER_UNKNOWN; + mi->total = CAMEL_STORE_INFO_FOLDER_UNKNOWN; return mi; } -static CamelFolderInfo * -folder_info_load(CamelStoreSummary *s, FILE *in) +static CamelStoreInfo * +store_info_load(CamelStoreSummary *s, FILE *in) { - CamelFolderInfo *mi; + CamelStoreInfo *mi; mi = camel_store_summary_info_new(s); io(printf("Loading folder info\n")); - camel_file_util_decode_string(in, &mi->full); + camel_file_util_decode_string(in, &mi->path); camel_file_util_decode_uint32(in, &mi->flags); camel_file_util_decode_uint32(in, &mi->unread); camel_file_util_decode_uint32(in, &mi->total); @@ -802,11 +822,11 @@ folder_info_load(CamelStoreSummary *s, FILE *in) } static int -folder_info_save(CamelStoreSummary *s, FILE *out, CamelFolderInfo *mi) +store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi) { io(printf("Saving folder info\n")); - camel_file_util_encode_string(out, camel_folder_info_full(s, mi)); + camel_file_util_encode_string(out, camel_store_info_path(s, mi)); camel_file_util_encode_uint32(out, mi->flags); camel_file_util_encode_uint32(out, mi->unread); camel_file_util_encode_uint32(out, mi->total); @@ -815,15 +835,15 @@ folder_info_save(CamelStoreSummary *s, FILE *out, CamelFolderInfo *mi) } static void -folder_info_free(CamelStoreSummary *s, CamelFolderInfo *mi) +store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi) { - g_free(mi->full); + g_free(mi->path); g_free(mi->uri); - e_memchunk_free(s->folder_info_chunks, mi); + e_memchunk_free(s->store_info_chunks, mi); } static const char * -folder_info_string(CamelStoreSummary *s, const CamelFolderInfo *mi, int type) +store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) { const char *p; @@ -832,26 +852,30 @@ folder_info_string(CamelStoreSummary *s, const CamelFolderInfo *mi, int type) g_assert (mi != NULL); switch (type) { - case CAMEL_STORE_SUMMARY_FULL: - return mi->full; - case CAMEL_STORE_SUMMARY_NAME: - p = strrchr(mi->full, '/'); + case CAMEL_STORE_INFO_PATH: + return mi->path; + case CAMEL_STORE_INFO_NAME: + p = strrchr(mi->path, '/'); if (p) return p; else - return mi->full; - case CAMEL_STORE_SUMMARY_URI: - if (mi->uri) - return mi->uri; - if (s->uri_prefix) - return (((CamelFolderInfo *)mi)->uri = g_strdup_printf("%s%s", s->uri_prefix, mi->full)); + return mi->path; + case CAMEL_STORE_INFO_URI: + if (mi->uri == NULL) { + CamelURL *uri; + + uri = camel_url_new_with_base(s->uri_base, mi->path); + ((CamelStoreInfo *)mi)->uri = camel_url_to_string(uri, 0); + camel_url_free(uri); + } + return mi->uri; } return ""; } static void -folder_info_set_string (CamelStoreSummary *s, CamelFolderInfo *mi, int type, const char *str) +store_info_set_string (CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str) { const char *p; char *v; @@ -860,40 +884,36 @@ folder_info_set_string (CamelStoreSummary *s, CamelFolderInfo *mi, int type, con g_assert (mi != NULL); switch(type) { - case CAMEL_STORE_SUMMARY_FULL: - g_free(mi->full); + case CAMEL_STORE_INFO_PATH: + CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); + g_hash_table_remove(s->folders_path, (char *)camel_store_info_path(s, mi)); + g_free(mi->path); g_free(mi->uri); - mi->full = g_strdup(str); + mi->path = g_strdup(str); + g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, mi), mi); + s->flags |= CAMEL_STORE_SUMMARY_DIRTY; + CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); break; - case CAMEL_STORE_SUMMARY_NAME: - p = strrchr(mi->full, '/'); + case CAMEL_STORE_INFO_NAME: + CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); + g_hash_table_remove(s->folders_path, (char *)camel_store_info_path(s, mi)); + p = strrchr(mi->path, '/'); if (p) { - len = p-mi->full+1; + len = p-mi->path+1; v = g_malloc(len+strlen(str)+1); - memcpy(v, mi->full, len); + memcpy(v, mi->path, len); strcpy(v+len, str); } else { v = g_strdup(str); } - g_free(mi->full); - mi->full = v; + g_free(mi->path); + mi->path = v; + g_hash_table_insert(s->folders_path, (char *)camel_store_info_path(s, mi), mi); + CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); break; - case CAMEL_STORE_SUMMARY_URI: - if (s->uri_prefix) { - len = strlen(s->uri_prefix); - if (len > strlen(str) - || strncmp(s->uri_prefix, str, len) != 0) { - g_warning("Trying to set folderinfo uri '%s' for summary with prefix '%s'", - str, s->uri_prefix); - return; - } - g_free(mi->full); - g_free(mi->uri); - mi->full = g_strdup(str + len); - mi->uri = g_strdup(str); - } else { - g_warning("Trying to set folderinfo uri '%s' for summary with no uri prefix", str); - } + case CAMEL_STORE_INFO_URI: + g_warning("Cannot set store info uri, aborting"); + abort(); break; } } diff --git a/camel/camel-store-summary.h b/camel/camel-store-summary.h index a26d2fa84c..dbcd6119b7 100644 --- a/camel/camel-store-summary.h +++ b/camel/camel-store-summary.h @@ -29,44 +29,42 @@ extern "C" { #endif /* __cplusplus */ #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> + +#include <glib.h> #include <camel/camel-mime-parser.h> #include <camel/camel-object.h> +#include <camel/camel-url.h> #define CAMEL_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_store_summary_get_type (), CamelStoreSummary) #define CAMEL_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_store_summary_get_type (), CamelStoreSummaryClass) -#define CAMEL_IS_FOLDER_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_store_summary_get_type ()) +#define CAMEL_IS_STORE_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_store_summary_get_type ()) typedef struct _CamelStoreSummary CamelStoreSummary; typedef struct _CamelStoreSummaryClass CamelStoreSummaryClass; -typedef struct _CamelFolderInfo CamelFolderInfo; +typedef struct _CamelStoreInfo CamelStoreInfo; -enum _CamelFolderFlags { - CAMEL_STORE_SUMMARY_FOLDER_NOSELECT, - CAMEL_STORE_SUMMARY_FOLDER_READONLY, - CAMEL_STORE_SUMMARY_FOLDER_SUBSCRIBED, - CAMEL_STORE_SUMMARY_FOLDER_FLAGGED, +enum _CamelStoreInfoFlags { + CAMEL_STORE_INFO_FOLDER_NOSELECT, + CAMEL_STORE_INFO_FOLDER_READONLY, + CAMEL_STORE_INFO_FOLDER_SUBSCRIBED, + CAMEL_STORE_INFO_FOLDER_FLAGGED, }; -#define CAMEL_STORE_SUMMARY_UNKNOWN (~0) +#define CAMEL_STORE_INFO_FOLDER_UNKNOWN (~0) enum { - CAMEL_STORE_SUMMARY_FULL = 0, - CAMEL_STORE_SUMMARY_NAME, - CAMEL_STORE_SUMMARY_URI, - CAMEL_STORE_SUMMARY_LAST, + CAMEL_STORE_INFO_PATH = 0, + CAMEL_STORE_INFO_NAME, + CAMEL_STORE_INFO_URI, + CAMEL_STORE_INFO_LAST, }; -struct _CamelFolderInfo { +struct _CamelStoreInfo { guint32 refcount; char *uri; - char *full; + char *path; guint32 flags; guint32 unread; guint32 total; @@ -74,6 +72,7 @@ struct _CamelFolderInfo { enum _CamelStoreSummaryFlags { CAMEL_STORE_SUMMARY_DIRTY = 1<<0, + CAMEL_STORE_SUMMARY_FRAGMENT = 1<<1, /* path name is stored in fragment rather than path */ }; struct _CamelStoreSummary { @@ -82,22 +81,22 @@ struct _CamelStoreSummary { struct _CamelStoreSummaryPrivate *priv; /* header info */ - guint32 version; /* version of file required, should be set by implementors */ + guint32 version; /* version of base part of file */ guint32 flags; /* flags */ guint32 count; /* how many were saved/loaded */ time_t time; /* timestamp for this summary (for implementors to use) */ + struct _CamelURL *uri_base; /* url of base part of summary */ /* sizes of memory objects */ - guint32 folder_info_size; + guint32 store_info_size; /* memory allocators (setup automatically) */ - struct _EMemChunk *folder_info_chunks; + struct _EMemChunk *store_info_chunks; char *summary_path; - char *uri_prefix; - GPtrArray *folders; /* CamelFolderInfo's */ - GHashTable *folders_full; /* CamelFolderInfo's by full name */ + GPtrArray *folders; /* CamelStoreInfo's */ + GHashTable *folders_path; /* CamelStoreInfo's by path name */ }; struct _CamelStoreSummaryClass { @@ -108,21 +107,21 @@ struct _CamelStoreSummaryClass { int (*summary_header_save)(CamelStoreSummary *, FILE *); /* create/save/load an individual message info */ - CamelFolderInfo * (*folder_info_new)(CamelStoreSummary *, const char *full); - CamelFolderInfo * (*folder_info_load)(CamelStoreSummary *, FILE *); - int (*folder_info_save)(CamelStoreSummary *, FILE *, CamelFolderInfo *); - void (*folder_info_free)(CamelStoreSummary *, CamelFolderInfo *); + CamelStoreInfo * (*store_info_new)(CamelStoreSummary *, const char *path); + CamelStoreInfo * (*store_info_load)(CamelStoreSummary *, FILE *); + int (*store_info_save)(CamelStoreSummary *, FILE *, CamelStoreInfo *); + void (*store_info_free)(CamelStoreSummary *, CamelStoreInfo *); /* virtualise access methods */ - const char *(*folder_info_string)(CamelStoreSummary *, const CamelFolderInfo *, int); - void (*folder_info_set_string)(CamelStoreSummary *, CamelFolderInfo *, int, const char *); + const char *(*store_info_string)(CamelStoreSummary *, const CamelStoreInfo *, int); + void (*store_info_set_string)(CamelStoreSummary *, CamelStoreInfo *, int, const char *); }; CamelType camel_store_summary_get_type (void); CamelStoreSummary *camel_store_summary_new (void); void camel_store_summary_set_filename(CamelStoreSummary *, const char *); -void camel_store_summary_set_uri_prefix(CamelStoreSummary *, const char *); +void camel_store_summary_set_uri_base(CamelStoreSummary *s, CamelURL *base); /* load/save the summary in its entirety */ int camel_store_summary_load(CamelStoreSummary *); @@ -135,21 +134,21 @@ int camel_store_summary_header_load(CamelStoreSummary *); void camel_store_summary_touch(CamelStoreSummary *s); /* add a new raw summary item */ -void camel_store_summary_add(CamelStoreSummary *, CamelFolderInfo *info); +void camel_store_summary_add(CamelStoreSummary *, CamelStoreInfo *info); /* build/add raw summary items */ -CamelFolderInfo *camel_store_summary_add_from_full(CamelStoreSummary *, const char *); +CamelStoreInfo *camel_store_summary_add_from_path(CamelStoreSummary *, const char *); /* Just build raw summary items */ -CamelFolderInfo *camel_store_summary_info_new(CamelStoreSummary *s); -CamelFolderInfo *camel_store_summary_info_new_from_full(CamelStoreSummary *s, const char *); +CamelStoreInfo *camel_store_summary_info_new(CamelStoreSummary *s); +CamelStoreInfo *camel_store_summary_info_new_from_path(CamelStoreSummary *s, const char *); -void camel_store_summary_info_ref(CamelStoreSummary *, CamelFolderInfo *); -void camel_store_summary_info_free(CamelStoreSummary *, CamelFolderInfo *); +void camel_store_summary_info_ref(CamelStoreSummary *, CamelStoreInfo *); +void camel_store_summary_info_free(CamelStoreSummary *, CamelStoreInfo *); /* removes a summary item */ -void camel_store_summary_remove(CamelStoreSummary *s, CamelFolderInfo *info); -void camel_store_summary_remove_full(CamelStoreSummary *s, const char *full); +void camel_store_summary_remove(CamelStoreSummary *s, CamelStoreInfo *info); +void camel_store_summary_remove_path(CamelStoreSummary *s, const char *path); void camel_store_summary_remove_index(CamelStoreSummary *s, int); /* remove all items */ @@ -157,18 +156,18 @@ void camel_store_summary_clear(CamelStoreSummary *s); /* lookup functions */ int camel_store_summary_count(CamelStoreSummary *); -CamelFolderInfo *camel_store_summary_index(CamelStoreSummary *, int); -CamelFolderInfo *camel_store_summary_full(CamelStoreSummary *, const char *uid); +CamelStoreInfo *camel_store_summary_index(CamelStoreSummary *, int); +CamelStoreInfo *camel_store_summary_path(CamelStoreSummary *, const char *uid); GPtrArray *camel_store_summary_array(CamelStoreSummary *s); void camel_store_summary_array_free(CamelStoreSummary *s, GPtrArray *array); -const char *camel_folder_info_string(CamelStoreSummary *, const CamelFolderInfo *, int type); -void camel_folder_info_set_string(CamelStoreSummary *, CamelFolderInfo *, int type, const char *value); +const char *camel_store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int type); +void camel_store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int type, const char *value); /* helper macro's */ -#define camel_folder_info_full(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_FULL)) -#define camel_folder_info_uri(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_URI)) -#define camel_folder_info_name(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_NAME)) +#define camel_store_info_path(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_STORE_INFO_PATH)) +#define camel_store_info_uri(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_STORE_INFO_URI)) +#define camel_store_info_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_STORE_INFO_NAME)) #ifdef __cplusplus } diff --git a/camel/camel-store.h b/camel/camel-store.h index e054ab7224..29fb3cc95e 100644 --- a/camel/camel-store.h +++ b/camel/camel-store.h @@ -62,6 +62,8 @@ typedef struct _CamelFolderInfo { #define CAMEL_FOLDER_NOINFERIORS (1<<1) /* a folder which has children (not yet fully implemented) */ #define CAMEL_FOLDER_CHILDREN (1<<2) +/* a folder which is subscribed */ +#define CAMEL_FOLDER_SUBSCRIBED (1<<3) /* Structure of rename event's event_data */ typedef struct _CamelRenameInfo { diff --git a/camel/camel-url.h b/camel/camel-url.h index d60c9594c2..6bce26c8ad 100644 --- a/camel/camel-url.h +++ b/camel/camel-url.h @@ -35,7 +35,7 @@ extern "C" { #pragma } #endif /* __cplusplus */ -typedef struct { +typedef struct _CamelURL { char *protocol; char *user; char *authmech; diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am index dbee888f39..469964a9a6 100644 --- a/camel/providers/imap/Makefile.am +++ b/camel/providers/imap/Makefile.am @@ -24,6 +24,7 @@ libcamelimap_la_SOURCES = \ camel-imap-provider.c \ camel-imap-search.c \ camel-imap-store.c \ + camel-imap-store-summary.c \ camel-imap-summary.c \ camel-imap-utils.c \ camel-imap-wrapper.c @@ -34,6 +35,7 @@ libcamelimapinclude_HEADERS = \ camel-imap-message-cache.h \ camel-imap-search.h \ camel-imap-store.h \ + camel-imap-store-summary.h \ camel-imap-summary.h \ camel-imap-types.h \ camel-imap-utils.h \ diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index ef7dd1ba35..e3581c80af 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -37,9 +37,11 @@ #include "camel-imap-utils.h" #include "camel-imap-folder.h" #include "camel-imap-store.h" +#include "camel-imap-store-summary.h" #include "camel-imap-private.h" #include <camel/camel-exception.h> #include <camel/camel-private.h> +#include <camel/camel-utf8.h> #define d(x) x @@ -764,8 +766,10 @@ imap_command_strdup_vprintf (CamelImapStore *store, const char *fmt, case 'S': case 'F': string = args->pdata[i++]; - if (*p == 'F') - string = imap_mailbox_encode (string, strlen (string)); + if (*p == 'F') { + char *s = camel_imap_store_summary_full_from_path(store->summary, string); + string = s?s:camel_utf8_utf7(string); + } if (imap_is_atom (string)) { outptr += sprintf (outptr, "%s", string); diff --git a/camel/providers/imap/camel-imap-store-summary.c b/camel/providers/imap/camel-imap-store-summary.c new file mode 100644 index 0000000000..4ba5bb9b68 --- /dev/null +++ b/camel/providers/imap/camel-imap-store-summary.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2002 Ximian Inc. + * + * Authors: Michael Zucchi <notzed@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include "camel-imap-store-summary.h" + +#include "camel-file-utils.h" + +#include "hash-table-utils.h" +#include "e-util/md5-utils.h" +#include "e-util/e-memory.h" + +#include "camel-private.h" +#include "camel-utf8.h" + +#define d(x) +#define io(x) /* io debug */ + +#define CAMEL_IMAP_STORE_SUMMARY_VERSION_0 (0) + +#define CAMEL_IMAP_STORE_SUMMARY_VERSION (0) + +#define _PRIVATE(o) (((CamelImapStoreSummary *)(o))->priv) + +static int summary_header_load(CamelStoreSummary *, FILE *); +static int summary_header_save(CamelStoreSummary *, FILE *); + +/*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/ +static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *); +static int store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *); +static void store_info_free(CamelStoreSummary *, CamelStoreInfo *); + +static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int); +static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *); + +static void camel_imap_store_summary_class_init (CamelImapStoreSummaryClass *klass); +static void camel_imap_store_summary_init (CamelImapStoreSummary *obj); +static void camel_imap_store_summary_finalise (CamelObject *obj); + +static CamelStoreSummaryClass *camel_imap_store_summary_parent; + +static void +camel_imap_store_summary_class_init (CamelImapStoreSummaryClass *klass) +{ + CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass; + + ssklass->summary_header_load = summary_header_load; + ssklass->summary_header_save = summary_header_save; + + /*ssklass->store_info_new = store_info_new;*/ + ssklass->store_info_load = store_info_load; + ssklass->store_info_save = store_info_save; + ssklass->store_info_free = store_info_free; + + ssklass->store_info_string = store_info_string; + ssklass->store_info_set_string = store_info_set_string; +} + +static void +camel_imap_store_summary_init (CamelImapStoreSummary *s) +{ + /*struct _CamelImapStoreSummaryPrivate *p; + + p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/ + + ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelImapStoreInfo); + s->version = CAMEL_IMAP_STORE_SUMMARY_VERSION; +} + +static void +camel_imap_store_summary_finalise (CamelObject *obj) +{ + /*struct _CamelImapStoreSummaryPrivate *p;*/ + /*CamelImapStoreSummary *s = (CamelImapStoreSummary *)obj;*/ + + /*p = _PRIVATE(obj); + g_free(p);*/ +} + +CamelType +camel_imap_store_summary_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + camel_imap_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type(); + type = camel_type_register((CamelType)camel_imap_store_summary_parent, "CamelImapStoreSummary", + sizeof (CamelImapStoreSummary), + sizeof (CamelImapStoreSummaryClass), + (CamelObjectClassInitFunc) camel_imap_store_summary_class_init, + NULL, + (CamelObjectInitFunc) camel_imap_store_summary_init, + (CamelObjectFinalizeFunc) camel_imap_store_summary_finalise); + } + + return type; +} + +/** + * camel_imap_store_summary_new: + * + * Create a new CamelImapStoreSummary object. + * + * Return value: A new CamelImapStoreSummary widget. + **/ +CamelImapStoreSummary * +camel_imap_store_summary_new (void) +{ + CamelImapStoreSummary *new = CAMEL_IMAP_STORE_SUMMARY ( camel_object_new (camel_imap_store_summary_get_type ())); + + return new; +} + +/** + * camel_imap_store_summary_full_name: + * @s: + * @path: + * + * Retrieve a summary item by full name. + * + * A referenced to the summary item is returned, which may be + * ref'd or free'd as appropriate. + * + * Return value: The summary item, or NULL if the @full_name name + * is not available. + * It must be freed using camel_store_summary_info_free(). + **/ +CamelImapStoreInfo * +camel_imap_store_summary_full_name(CamelImapStoreSummary *s, const char *full_name) +{ + int count, i; + CamelImapStoreInfo *info; + + count = camel_store_summary_count((CamelStoreSummary *)s); + for (i=0;i<count;i++) { + info = (CamelImapStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i); + if (info) { + if (strcmp(info->full_name, full_name) == 0) + return info; + camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info); + } + } + + return NULL; +} + +char * +camel_imap_store_summary_full_to_path(CamelImapStoreSummary *s, const char *full_name, char dir_sep) +{ + char *path, *p; + int c; + const char *f; + + if (dir_sep != '/') { + p = path = alloca(strlen(full_name)*3+1); + f = full_name; + while ( (c = *f++ & 0xff) ) { + if (c == dir_sep) + *p++ = '/'; + else if (c == '/' || c == '%') + p += sprintf(p, "%%%02X", c); + else + *p++ = c; + } + *p = 0; + } else + path = (char *)full_name; + + return camel_utf7_utf8(path); +} + +static guint32 hexnib(guint32 c) +{ + if (c >= '0' && c <= '9') + return c-'0'; + else if (c>='A' && c <= 'Z') + return c-'A'+10; + else + return 0; +} + +char * +camel_imap_store_summary_path_to_full(CamelImapStoreSummary *s, const char *path, char dir_sep) +{ + unsigned char *full, *f; + guint32 c, v = 0; + const char *p; + int state=0; + char *subpath, *last = NULL; + CamelStoreInfo *si; + + /* check to see if we have a subpath of path already defined */ + subpath = alloca(strlen(path)+1); + strcpy(subpath, path); + do { + si = camel_store_summary_path((CamelStoreSummary *)s, subpath); + if (si == NULL) { + last = strrchr(subpath, '/'); + if (last) + *last = 0; + } + } while (si == NULL && last); + + /* path is already present, use the raw version we have */ + if (si && strlen(subpath) == strlen(path)) { + f = g_strdup(camel_imap_store_info_full_name(s, si)); + camel_store_summary_info_free((CamelStoreSummary *)s, si); + return f; + } + + f = full = alloca(strlen(path)*2+1); + if (si) + p = path + strlen(subpath); + else + p = path; + while ( (c = camel_utf8_getc((const unsigned char **)&p)) ) { + switch(state) { + case 0: + if (c == '%') + state = 1; + else { + if (c == '/') + c = dir_sep; + camel_utf8_putc(&f, c); + } + break; + case 1: + state = 2; + v = hexnib(c)<<4; + break; + case 2: + state = 0; + v |= hexnib(c); + camel_utf8_putc(&f, v); + break; + } + } + camel_utf8_putc(&f, c); + + /* merge old path part if required */ + f = camel_utf8_utf7(full); + if (si) { + full = g_strdup_printf("%s%s", camel_imap_store_info_full_name(s, si), f); + g_free(f); + camel_store_summary_info_free((CamelStoreSummary *)s, si); + f = full; + } + + return f; +} + +CamelImapStoreInfo * +camel_imap_store_summary_add_from_full(CamelImapStoreSummary *s, const char *full_name, char dir_sep) +{ + CamelImapStoreInfo *info; + char *pathu8; + + d(printf("adding full name '%s' '%c'\n", full_name, dir_sep)); + + info = camel_imap_store_summary_full_name(s, full_name); + if (info) { + camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info); + d(printf(" already there\n")); + return info; + } + + pathu8 = camel_imap_store_summary_full_to_path(s, full_name, dir_sep); + + info = (CamelImapStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8); + if (info) { + d(printf(" '%s' -> '%s'\n", pathu8, full_name)); + camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_IMAP_STORE_INFO_FULL_NAME, full_name); + } else + d(printf(" failed\n")); + + return info; +} + +/* should this be const? */ +char * +camel_imap_store_summary_full_from_path(CamelImapStoreSummary *s, const char *path) +{ + CamelImapStoreInfo *si; + + si = (CamelImapStoreInfo *)camel_store_summary_path((CamelStoreSummary *)s, path); + + d(printf("looking up path %s -> %s\n", path, si?si->full_name:"not found")); + + if (si) + return g_strdup(si->full_name); + + return NULL; +} + +/* TODO: this api needs some more work */ +CamelImapStoreNamespace *camel_imap_store_summary_namespace_new(CamelImapStoreSummary *s, const char *full_name, char dir_sep) +{ + CamelImapStoreNamespace *ns; + + ns = g_malloc0(sizeof(*ns)); + ns->full_name = g_strdup(full_name); + ns->sep = dir_sep; + ns->path = camel_imap_store_summary_full_to_path(s, full_name, dir_sep); + + return ns; +} + +void camel_imap_store_summary_namespace_set(CamelImapStoreSummary *s, CamelImapStoreNamespace *ns) +{ + static void namespace_clear(CamelStoreSummary *s); + + namespace_clear((CamelStoreSummary *)s); + s->namespace = ns; + camel_store_summary_touch((CamelStoreSummary *)s); +} + +static void +namespace_free(CamelStoreSummary *s, CamelImapStoreNamespace *ns) +{ + g_free(ns->path); + g_free(ns->full_name); + g_free(ns); +} + +static void +namespace_clear(CamelStoreSummary *s) +{ + CamelImapStoreSummary *is = (CamelImapStoreSummary *)s; + + if (is->namespace) + namespace_free(s, is->namespace); + is->namespace = NULL; +} + +static CamelImapStoreNamespace * +namespace_load(CamelStoreSummary *s, FILE *in) +{ + CamelImapStoreNamespace *ns; + guint32 sep = '/'; + + ns = g_malloc0(sizeof(*ns)); + if (camel_file_util_decode_string(in, &ns->path) == -1 + || camel_file_util_decode_string(in, &ns->full_name) == -1 + || camel_file_util_decode_uint32(in, &sep) == -1) { + namespace_free(s, ns); + ns = NULL; + } else { + ns->sep = sep; + } + + return ns; +} + +static int +namespace_save(CamelStoreSummary *s, FILE *in, CamelImapStoreNamespace *ns) +{ + if (camel_file_util_encode_string(in, ns->path) == -1 + || camel_file_util_encode_string(in, ns->full_name) == -1 + || camel_file_util_encode_uint32(in, (guint32)ns->sep) == -1) + return -1; + + return 0; +} + +static int +summary_header_load(CamelStoreSummary *s, FILE *in) +{ + CamelImapStoreSummary *is = (CamelImapStoreSummary *)s; + gint32 version, capabilities, count; + + namespace_clear(s); + + if (camel_imap_store_summary_parent->summary_header_load((CamelStoreSummary *)s, in) == -1 + || camel_file_util_decode_fixed_int32(in, &version) == -1) + return -1; + + is->version = version; + + if (version < CAMEL_IMAP_STORE_SUMMARY_VERSION_0) { + g_warning("Store summary header version too low"); + return -1; + } + + /* note file format can be expanded to contain more namespaces, but only 1 at the moment */ + if (camel_file_util_decode_fixed_int32(in, &capabilities) == -1 + || camel_file_util_decode_fixed_int32(in, &count) == -1 + || count > 1) + return -1; + + is->capabilities = capabilities; + if (count == 1) { + if ((is->namespace = namespace_load(s, in)) == NULL) + return -1; + } + + return 0; +} + +static int +summary_header_save(CamelStoreSummary *s, FILE *out) +{ + CamelImapStoreSummary *is = (CamelImapStoreSummary *)s; + guint32 count; + + count = is->namespace?1:0; + + /* always write as latest version */ + if (camel_imap_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1 + || camel_file_util_encode_fixed_int32(out, CAMEL_IMAP_STORE_SUMMARY_VERSION) == -1 + || camel_file_util_encode_fixed_int32(out, is->capabilities) == -1 + || camel_file_util_encode_fixed_int32(out, count) == -1) + return -1; + + if (is->namespace && namespace_save(s, out, is->namespace) == -1) + return -1; + + return 0; +} + +static CamelStoreInfo * +store_info_load(CamelStoreSummary *s, FILE *in) +{ + CamelImapStoreInfo *mi; + + mi = (CamelImapStoreInfo *)camel_imap_store_summary_parent->store_info_load(s, in); + if (mi) { + if (camel_file_util_decode_string(in, &mi->full_name) == -1) { + camel_store_summary_info_free(s, (CamelStoreInfo *)mi); + mi = NULL; + } + } + + return (CamelStoreInfo *)mi; +} + +static int +store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi) +{ + CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi; + + if (camel_imap_store_summary_parent->store_info_save(s, out, mi) == -1 + || camel_file_util_encode_string(out, isi->full_name) == -1) + return -1; + + return 0; +} + +static void +store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi) +{ + CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi; + + g_free(isi->full_name); + camel_imap_store_summary_parent->store_info_free(s, mi); +} + +static const char * +store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) +{ + CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi; + + /* FIXME: Locks? */ + + g_assert (mi != NULL); + + switch (type) { + case CAMEL_IMAP_STORE_INFO_FULL_NAME: + return isi->full_name; + default: + return camel_imap_store_summary_parent->store_info_string(s, mi, type); + } +} + +static void +store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str) +{ + CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi; + + g_assert(mi != NULL); + + switch(type) { + case CAMEL_IMAP_STORE_INFO_FULL_NAME: + d(printf("Set full name %s -> %s\n", isi->full_name, str)); + CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); + g_free(isi->full_name); + isi->full_name = g_strdup(str); + CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); + break; + default: + camel_imap_store_summary_parent->store_info_set_string(s, mi, type, str); + break; + } +} diff --git a/camel/providers/imap/camel-imap-store-summary.h b/camel/providers/imap/camel-imap-store-summary.h new file mode 100644 index 0000000000..013283b5c1 --- /dev/null +++ b/camel/providers/imap/camel-imap-store-summary.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 Ximian Inc. + * + * Authors: Michael Zucchi <notzed@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _CAMEL_IMAP_STORE_SUMMARY_H +#define _CAMEL_IMAP_STORE_SUMMARY_H + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#include <camel/camel-object.h> +#include <camel/camel-store-summary.h> + +#define CAMEL_IMAP_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_imap_store_summary_get_type (), CamelImapStoreSummary) +#define CAMEL_IMAP_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imap_store_summary_get_type (), CamelImapStoreSummaryClass) +#define CAMEL_IS_IMAP_STORE_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_imap_store_summary_get_type ()) + +typedef struct _CamelImapStoreSummary CamelImapStoreSummary; +typedef struct _CamelImapStoreSummaryClass CamelImapStoreSummaryClass; + +typedef struct _CamelImapStoreInfo CamelImapStoreInfo; + +enum { + CAMEL_IMAP_STORE_INFO_FULL_NAME = CAMEL_STORE_INFO_LAST, + CAMEL_IMAP_STORE_INFO_LAST, +}; + +struct _CamelImapStoreInfo { + CamelStoreInfo info; + char *full_name; +}; + +typedef struct _CamelImapStoreNamespace CamelImapStoreNamespace; + +struct _CamelImapStoreNamespace { + char *path; /* display path */ + char *full_name; /* real name */ + char sep; /* directory separator */ +}; + +struct _CamelImapStoreSummary { + CamelStoreSummary summary; + + struct _CamelImapStoreSummaryPrivate *priv; + + /* header info */ + guint32 version; /* version of base part of file */ + guint32 capabilities; + CamelImapStoreNamespace *namespace; /* eventually to be a list */ +}; + +struct _CamelImapStoreSummaryClass { + CamelStoreSummaryClass summary_class; +}; + +CamelType camel_imap_store_summary_get_type (void); +CamelImapStoreSummary *camel_imap_store_summary_new (void); + +/* TODO: this api needs some more work */ +CamelImapStoreNamespace *camel_imap_store_summary_namespace_new(CamelImapStoreSummary *s, const char *full_name, char dir_sep); +void camel_imap_store_summary_namespace_set(CamelImapStoreSummary *s, CamelImapStoreNamespace *ns); + +/* converts to/from utf8 canonical nasmes */ +char *camel_imap_store_summary_full_to_path(CamelImapStoreSummary *s, const char *full_name, char dir_sep); +char *camel_imap_store_summary_path_to_full(CamelImapStoreSummary *s, const char *path, char dir_sep); + +CamelImapStoreInfo *camel_imap_store_summary_full_name(CamelImapStoreSummary *s, const char *full_name); +CamelImapStoreInfo *camel_imap_store_summary_add_from_full(CamelImapStoreSummary *s, const char *full_name, char dir_sep); + +/* a convenience lookup function. always use this if path known */ +char *camel_imap_store_summary_full_from_path(CamelImapStoreSummary *s, const char *path); + +/* helper macro's */ +#define camel_imap_store_info_full_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_IMAP_STORE_INFO_FULL_NAME)) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ! _CAMEL_IMAP_STORE_SUMMARY_H */ diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index c56976466d..7eb3c3accb 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -38,6 +38,7 @@ #include "e-util/e-path.h" #include "camel-imap-store.h" +#include "camel-imap-store-summary.h" #include "camel-imap-folder.h" #include "camel-imap-utils.h" #include "camel-imap-command.h" @@ -55,6 +56,7 @@ #include "camel-tcp-stream-ssl.h" #include "camel-url.h" #include "camel-sasl.h" +#include "camel-utf8.h" #include "string-utils.h" #include "camel-imap-private.h" @@ -114,14 +116,10 @@ static void get_folders_online (CamelImapStore *imap_store, const char *pattern, GPtrArray *folders, gboolean lsub, CamelException *ex); -static void imap_folder_effectively_unsubscribed(CamelImapStore *imap_store, - const char *folder_name, CamelException *ex); - -static gboolean imap_check_folder_still_extant (CamelImapStore *imap_store, const char *full_name, - CamelException *ex); - -static void imap_forget_folder(CamelImapStore *imap_store, const char *folder_name, - CamelException *ex); +static void imap_folder_effectively_unsubscribed(CamelImapStore *imap_store, const char *folder_name, CamelException *ex); +static gboolean imap_check_folder_still_extant (CamelImapStore *imap_store, const char *full_name, CamelException *ex); +static void imap_forget_folder(CamelImapStore *imap_store, const char *folder_name, CamelException *ex); +static void imap_set_server_level (CamelImapStore *store); static void camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) @@ -180,6 +178,11 @@ static void camel_imap_store_finalize (CamelObject *object) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (object); + + if (imap_store->summary) { + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); + camel_object_unref(imap_store->summary); + } if (imap_store->istream) camel_object_unref (CAMEL_OBJECT (imap_store->istream)); @@ -254,7 +257,6 @@ camel_imap_store_init (gpointer object, gpointer klass) imap_store->dir_sep = '\0'; imap_store->current_folder = NULL; imap_store->connected = FALSE; - imap_store->subscribed_folders = NULL; imap_store->tag_prefix = imap_tag_prefix++; if (imap_tag_prefix > 'Z') @@ -294,6 +296,8 @@ construct (CamelService *service, CamelSession *session, { CamelImapStore *imap_store = CAMEL_IMAP_STORE (service); CamelStore *store = CAMEL_STORE (service); + char *tmp; + CamelURL *summary_url; CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); if (camel_exception_is_set (ex)) @@ -313,6 +317,7 @@ construct (CamelService *service, CamelSession *session, store->flags |= CAMEL_STORE_SUBSCRIPTIONS; if (camel_url_get_param (url, "namespace")) { imap_store->parameters |= IMAP_PARAM_OVERRIDE_NAMESPACE; + g_free(imap_store->namespace); imap_store->namespace = g_strdup (camel_url_get_param (url, "namespace")); } if (camel_url_get_param (url, "check_all")) @@ -321,6 +326,32 @@ construct (CamelService *service, CamelSession *session, imap_store->parameters |= IMAP_PARAM_FILTER_INBOX; store->flags |= CAMEL_STORE_FILTER_INBOX; } + + /* setup/load the store summary */ + tmp = alloca(strlen(imap_store->storage_path)+32); + sprintf(tmp, "%s/.ev-store-summary", imap_store->storage_path); + imap_store->summary = camel_imap_store_summary_new(); + camel_store_summary_set_filename((CamelStoreSummary *)imap_store->summary, tmp); + summary_url = camel_url_new(imap_store->base_url, NULL); + camel_store_summary_set_uri_base((CamelStoreSummary *)imap_store->summary, summary_url); + camel_url_free(summary_url); + if (camel_store_summary_load((CamelStoreSummary *)imap_store->summary) == 0) { + CamelImapStoreSummary *is = imap_store->summary; + + if (is->namespace) { + /* if namespace has changed, clear folder list */ + if (imap_store->namespace && strcmp(imap_store->namespace, is->namespace->full_name) != 0) { + camel_store_summary_clear((CamelStoreSummary *)is); + } else { + imap_store->namespace = g_strdup(is->namespace->full_name); + imap_store->dir_sep = is->namespace->sep; + store->dir_sep = is->namespace->sep; + } + } + + imap_store->capabilities = is->capabilities; + imap_set_server_level(imap_store); + } } static int @@ -502,6 +533,12 @@ imap_get_capability (CamelService *service, CamelException *ex) g_free (result); imap_set_server_level (store); + + if (store->summary->capabilities != store->capabilities) { + store->summary->capabilities = store->capabilities; + camel_store_summary_touch((CamelStoreSummary *)store->summary); + camel_store_summary_save((CamelStoreSummary *)store->summary); + } return TRUE; } @@ -761,40 +798,62 @@ query_auth_types (CamelService *service, CamelException *ex) return g_list_prepend (sasl_types, &camel_imap_password_authtype); } +/* folder_name is path name */ +static CamelFolderInfo * +imap_build_folder_info(CamelImapStore *imap_store, const char *folder_name) +{ + CamelURL *url; + const char *name; + CamelFolderInfo *fi; + + fi = g_malloc0(sizeof(*fi)); + + fi->full_name = g_strdup(folder_name); + fi->unread_message_count = 0; + + url = camel_url_new (imap_store->base_url, NULL); + g_free (url->path); + url->path = g_strdup_printf ("/%s", folder_name); + fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); + camel_url_free(url); + fi->path = g_strdup_printf("/%s", folder_name); + name = strrchr (fi->path, '/'); + if (name) + name++; + else + name = fi->path; + + fi->name = g_strdup (name); + + return fi; +} + static void imap_folder_effectively_unsubscribed(CamelImapStore *imap_store, const char *folder_name, CamelException *ex) { - gpointer key, value; CamelFolderInfo *fi; - const char *name; - - if (g_hash_table_lookup_extended (imap_store->subscribed_folders, - folder_name, &key, &value)) { - g_hash_table_remove (imap_store->subscribed_folders, key); - g_free (key); + CamelStoreInfo *si; + + si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name); + if (si) { + if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) { + si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + camel_store_summary_touch((CamelStoreSummary *)imap_store->summary); + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); + } + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); } - + if (imap_store->renaming) { /* we don't need to emit a "folder_unsubscribed" signal if we are in the process of renaming folders, so we are done here... */ return; + } - - name = strrchr (folder_name, imap_store->dir_sep); - if (name) - name++; - else - name = folder_name; - - fi = g_new0 (CamelFolderInfo, 1); - fi->full_name = g_strdup (folder_name); - fi->name = g_strdup (name); - fi->url = g_strdup_printf ("%s/%s", imap_store->base_url, folder_name); - fi->unread_message_count = -1; - camel_folder_info_build_path (fi, imap_store->dir_sep); - + + fi = imap_build_folder_info(imap_store, folder_name); camel_object_trigger_event (CAMEL_OBJECT (imap_store), "folder_unsubscribed", fi); camel_folder_info_free (fi); } @@ -809,7 +868,7 @@ imap_forget_folder (CamelImapStore *imap_store, const char *folder_name, CamelEx char *folder_dir, *storage_path; CamelFolderInfo *fi; const char *name; - + name = strrchr (folder_name, imap_store->dir_sep); if (name) name++; @@ -850,13 +909,11 @@ imap_forget_folder (CamelImapStore *imap_store, const char *folder_name, CamelEx g_free (folder_dir); event: - - fi = g_new0 (CamelFolderInfo, 1); - fi->full_name = g_strdup (folder_name); - fi->name = g_strdup (name); - fi->url = g_strdup_printf ("%s/%s", imap_store->base_url, folder_name); - fi->unread_message_count = -1; - camel_folder_info_build_path (fi, imap_store->dir_sep); + + camel_store_summary_remove_path((CamelStoreSummary *)imap_store->summary, folder_name); + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); + + fi = imap_build_folder_info(imap_store, folder_name); camel_object_trigger_event (CAMEL_OBJECT (imap_store), "folder_deleted", fi); camel_folder_info_free (fi); } @@ -1108,19 +1165,12 @@ imap_auth_loop (CamelService *service, CamelException *ex) return TRUE; } -#define IMAP_STOREINFO_VERSION 2 - static gboolean can_work_offline (CamelDiscoStore *disco_store) { CamelImapStore *store = CAMEL_IMAP_STORE (disco_store); - char *path; - gboolean can; - path = g_strdup_printf ("%s/storeinfo", store->storage_path); - can = access (path, F_OK) == 0; - g_free (path); - return can; + return camel_store_summary_count((CamelStoreSummary *)store->summary) != 0; } static gboolean @@ -1131,10 +1181,10 @@ imap_connect_online (CamelService *service, CamelException *ex) CamelImapResponse *response; struct _namespaces *namespaces; char *result, *name, *path; - FILE *storeinfo; - int i, flags; + int i; size_t len; - + CamelImapStoreNamespace *ns; + CAMEL_SERVICE_LOCK (store, connect_lock); if (!connect_to_server_wrapper (service, ex) || !imap_auth_loop (service, ex)) { @@ -1143,16 +1193,6 @@ imap_connect_online (CamelService *service, CamelException *ex) return FALSE; } - 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_file_util_encode_uint32 (storeinfo, IMAP_STOREINFO_VERSION); - camel_file_util_encode_uint32 (storeinfo, store->capabilities); - /* Get namespace and hierarchy separator */ if ((store->capabilities & IMAP_CAPABILITY_NAMESPACE) && !(store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE)) { @@ -1189,7 +1229,7 @@ imap_connect_online (CamelService *service, CamelException *ex) if (!store->namespace) store->namespace = g_strdup (""); - + if (!store->dir_sep) { if (store->server_level >= IMAP_LEVEL_IMAP4REV1) { /* This idiom means "tell me the hierarchy separator @@ -1230,32 +1270,24 @@ imap_connect_online (CamelService *service, CamelException *ex) g_free (store->namespace); store->namespace = tmp; } - - /* Write namespace/separator out */ - camel_file_util_encode_string (storeinfo, store->namespace); - camel_file_util_encode_uint32 (storeinfo, store->dir_sep); + + ns = camel_imap_store_summary_namespace_new(store->summary, store->namespace, store->dir_sep); + camel_imap_store_summary_namespace_set(store->summary, ns); if (CAMEL_STORE (store)->flags & CAMEL_STORE_SUBSCRIPTIONS) { - /* Get subscribed folders */ - response = camel_imap_command (store, NULL, ex, "LSUB \"\" \"*\""); - if (!response) - goto done; - 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 (store, result, &flags, NULL, &name)) - continue; - if (flags & (CAMEL_IMAP_FOLDER_MARKED | CAMEL_IMAP_FOLDER_UNMARKED)) + GPtrArray *folders; + + /* this pre-fills the summary, and checks that lsub is useful */ + folders = g_ptr_array_new(); + get_folders_online(store, "*", folders, TRUE, ex); + for (i=0;i<folders->len;i++) { + CamelFolderInfo *fi = folders->pdata[i]; + + if (fi->flags & (CAMEL_IMAP_FOLDER_MARKED | CAMEL_IMAP_FOLDER_UNMARKED)) store->capabilities |= IMAP_CAPABILITY_useful_lsub; - if (flags & CAMEL_FOLDER_NOSELECT) { - g_free (name); - continue; - } - g_hash_table_insert (store->subscribed_folders, name, - GINT_TO_POINTER (1)); - camel_file_util_encode_string (storeinfo, result); + camel_folder_info_free(fi); } - camel_imap_response_free (store, response); + g_ptr_array_free(folders, TRUE); } path = g_strdup_printf ("%s/journal", store->storage_path); @@ -1263,7 +1295,9 @@ imap_connect_online (CamelService *service, CamelException *ex) g_free (path); done: - fclose (storeinfo); + /* save any changes we had */ + camel_store_summary_save((CamelStoreSummary *)store->summary); + CAMEL_SERVICE_UNLOCK (store, connect_lock); if (camel_exception_is_set (ex)) @@ -1279,9 +1313,7 @@ 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; + char *path; path = g_strdup_printf ("%s/journal", store->storage_path); disco_store->diary = camel_disco_diary_new (disco_store, path, ex); @@ -1289,48 +1321,6 @@ imap_connect_offline (CamelService *service, CamelException *ex) if (!disco_store->diary) return FALSE; - path = g_strdup_printf ("%s/storeinfo", store->storage_path); - storeinfo = fopen (path, "r"); - g_free (path); - tmp = 0; - if (storeinfo) - camel_file_util_decode_uint32 (storeinfo, &tmp); - if (tmp != IMAP_STOREINFO_VERSION) { - if (storeinfo) - fclose (storeinfo); - - /* We know we're offline, so this will have to set @ex - * and return FALSE. - */ - return camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex); - } - - store->subscribed_folders = g_hash_table_new (g_str_hash, g_str_equal); - - camel_file_util_decode_uint32 (storeinfo, &store->capabilities); - imap_set_server_level (store); - camel_file_util_decode_string (storeinfo, &name); - /* if the namespace has changed, the subscribed folder list in this file is bogus */ - if (store->namespace == NULL || (name != NULL && strcmp(name, store->namespace) == 0)) { - g_free(store->namespace); - store->namespace = name; - camel_file_util_decode_uint32 (storeinfo, &tmp); - store->dir_sep = tmp; - ((CamelStore *)store)->dir_sep = tmp; - while (camel_file_util_decode_string (storeinfo, &buf) == 0) { - if (!imap_parse_list_response (store, buf, NULL, NULL, &name)) { - g_free (buf); - continue; - } - g_hash_table_insert (store->subscribed_folders, name, - GINT_TO_POINTER (1)); - g_free (buf); - } - } else { - g_free(name); - } - - fclose (storeinfo); imap_store_refresh_folders (store, ex); store->connected = !camel_exception_is_set (ex); @@ -1349,13 +1339,6 @@ imap_disconnect_offline (CamelService *service, gboolean clean, CamelException * store->current_folder = NULL; } - if (store->subscribed_folders) { - g_hash_table_foreach_remove (store->subscribed_folders, - free_key, NULL); - g_hash_table_destroy (store->subscribed_folders); - store->subscribed_folders = NULL; - } - if (store->authtypes) { g_hash_table_foreach_remove (store->authtypes, free_key, NULL); @@ -1529,29 +1512,34 @@ get_folder_online (CamelStore *store, const char *folder_name, if (!g_strcasecmp (folder_name, "INBOX")) folder_name = "INBOX"; - + /* Lock around the whole lot to check/create atomically */ CAMEL_SERVICE_LOCK (imap_store, connect_lock); if (imap_store->current_folder) { camel_object_unref (CAMEL_OBJECT (imap_store->current_folder)); imap_store->current_folder = NULL; } - response = camel_imap_command (imap_store, NULL, NULL, - "SELECT %F", folder_name); + response = camel_imap_command (imap_store, NULL, NULL, "SELECT %F", folder_name); if (!response) { + char *folder_real; + if (!flags & CAMEL_STORE_FOLDER_CREATE) { CAMEL_SERVICE_UNLOCK (imap_store, connect_lock); return no_such_folder (folder_name, ex); } - - response = camel_imap_command (imap_store, NULL, ex, - "CREATE %F", folder_name); + + folder_real = camel_imap_store_summary_path_to_full(imap_store->summary, folder_name, store->dir_sep); + + response = camel_imap_command (imap_store, NULL, ex, "CREATE %S", folder_real); + if (response) { + camel_imap_store_summary_add_from_full(imap_store->summary, folder_real, store->dir_sep); + camel_imap_response_free (imap_store, response); - response = camel_imap_command (imap_store, NULL, NULL, - "SELECT %F", folder_name); + response = camel_imap_command (imap_store, NULL, NULL, "SELECT %F", folder_name); } + g_free(folder_real); if (!response) { CAMEL_SERVICE_UNLOCK (imap_store, connect_lock); return NULL; @@ -1652,60 +1640,79 @@ delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) } static void -manage_subscriptions (CamelStore *store, CamelFolderInfo *fi, gboolean subscribe) +manage_subscriptions (CamelStore *store, const char *old_name, gboolean subscribe) { - while (fi) { - if (fi->child) - manage_subscriptions (store, fi->child, subscribe); - - if (subscribe) - subscribe_folder (store, fi->full_name, NULL); - else - unsubscribe_folder (store, fi->full_name, NULL); - - fi = fi->sibling; + CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); + CamelStoreInfo *si; + int olen = strlen(old_name); + const char *path; + int i, count; + + count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary); + for (i=0;i<count;i++) { + si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i); + if (si) { + path = camel_store_info_path(imap_store->summary, si); + if (strncmp(path, old_name, olen) == 0) { + if (subscribe) + subscribe_folder(store, path, NULL); + else + unsubscribe_folder(store, path, NULL); + } + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } } } -#define subscribe_folders(store, fi) manage_subscriptions (store, fi, TRUE) -#define unsubscribe_folders(store, fi) manage_subscriptions (store, fi, FALSE) - static void -rename_folder_info (CamelImapStore *imap_store, CamelFolderInfo *fi, const char *old_name, const char *new_name) +rename_folder_info (CamelImapStore *imap_store, const char *old_name, const char *new_name) { - CamelImapResponse *response; - char *name; - - while (fi) { - if (fi->child) - rename_folder_info (imap_store, fi->child, old_name, new_name); - - name = g_strdup_printf ("%s%s", new_name, fi->full_name + strlen (old_name)); - - if (imap_store->dir_sep == '.') { - /* kludge around imap servers like Courier that don't rename - subfolders when you rename the parent folder - like - the spec says to do!!! */ - response = camel_imap_command (imap_store, NULL, NULL, "RENAME %F %F", fi->full_name, name); - if (response) - camel_imap_response_free (imap_store, response); + int i, count; + CamelStoreInfo *si; + int olen = strlen(old_name); + const char *path; + char *npath, *nfull; + + count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary); + for (i=0;i<count;i++) { + si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i); + if (si == NULL) + continue; + path = camel_store_info_path(imap_store->summary, si); + if (strncmp(path, old_name, olen) == 0) { + if (strlen(path) > olen) + npath = g_strdup_printf("%s/%s", new_name, path+olen+1); + else + npath = g_strdup(new_name); + nfull = camel_imap_store_summary_path_to_full(imap_store->summary, npath, imap_store->dir_sep); + + /* workaround for broken server (courier uses '.') that doesn't rename + subordinate folders as required by rfc 2060 */ + if (imap_store->dir_sep == '.') { + CamelImapResponse *response; + + response = camel_imap_command (imap_store, NULL, NULL, "RENAME %F %S", path, nfull); + if (response) + camel_imap_response_free (imap_store, response); + } + + camel_store_info_set_string((CamelStoreSummary *)imap_store->summary, si, CAMEL_STORE_INFO_PATH, npath); + camel_store_info_set_string((CamelStoreSummary *)imap_store->summary, si, CAMEL_IMAP_STORE_INFO_FULL_NAME, nfull); + + camel_store_summary_touch((CamelStoreSummary *)imap_store->summary); + g_free(nfull); + g_free(npath); } - - g_free (fi->full_name); - fi->full_name = name; - - fi = fi->sibling; + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); } } static void -rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex) +rename_folder (CamelStore *store, const char *old_name, const char *new_name_in, CamelException *ex) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; - char *oldpath, *newpath, *storage_path; - CamelFolderInfo *fi; - guint32 flags; + char *oldpath, *newpath, *storage_path, *new_name; if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex)) return; @@ -1730,35 +1737,31 @@ rename_folder (CamelStore *store, const char *old_name, const char *new_name, Ca imap_store->renaming = TRUE; - flags = CAMEL_STORE_FOLDER_INFO_FAST | CAMEL_STORE_FOLDER_INFO_RECURSIVE | - (store->flags & CAMEL_STORE_SUBSCRIPTIONS ? CAMEL_STORE_FOLDER_INFO_SUBSCRIBED : 0); - - fi = ((CamelStoreClass *)((CamelObject *)store)->klass)->get_folder_info (store, old_name, flags, ex); - if (fi && store->flags & CAMEL_STORE_SUBSCRIPTIONS) - unsubscribe_folders (store, fi); - - response = camel_imap_command (imap_store, NULL, ex, "RENAME %F %F", old_name, new_name); + if (store->flags & CAMEL_STORE_SUBSCRIPTIONS) + manage_subscriptions(store, old_name, FALSE); + + new_name = camel_imap_store_summary_path_to_full(imap_store->summary, new_name_in, store->dir_sep); + response = camel_imap_command (imap_store, NULL, ex, "RENAME %F %S", old_name, new_name); if (!response) { - if (fi && store->flags & CAMEL_STORE_SUBSCRIPTIONS) - subscribe_folders (store, fi); - - camel_store_free_folder_info (store, fi); + if (store->flags & CAMEL_STORE_SUBSCRIPTIONS) + manage_subscriptions(store, old_name, TRUE); + g_free(new_name); imap_store->renaming = FALSE; return; } camel_imap_response_free (imap_store, response); - - rename_folder_info (imap_store, fi, old_name, new_name); - if (fi && store->flags & CAMEL_STORE_SUBSCRIPTIONS) - subscribe_folders (store, fi); - - camel_store_free_folder_info (store, fi); - + + /* rename summary, and handle broken server */ + rename_folder_info(imap_store, old_name, new_name_in); + + if (store->flags & CAMEL_STORE_SUBSCRIPTIONS) + manage_subscriptions(store, new_name_in, TRUE); + storage_path = g_strdup_printf("%s/folders", imap_store->storage_path); oldpath = e_path_to_physical (storage_path, old_name); - newpath = e_path_to_physical (storage_path, new_name); + newpath = e_path_to_physical (storage_path, new_name_in); g_free(storage_path); /* So do we care if this didn't work? Its just a cache? */ @@ -1769,7 +1772,8 @@ rename_folder (CamelStore *store, const char *old_name, const char *new_name, Ca g_free (oldpath); g_free (newpath); - + g_free(new_name); + imap_store->renaming = FALSE; } @@ -1778,13 +1782,11 @@ create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); - char *full_name, *resp, *thisone; + char *full_name, *resp, *thisone, *parent_real, *real_name; CamelImapResponse *response; CamelException internal_ex; CamelFolderInfo *root = NULL; gboolean need_convert; - char **pathnames = NULL; - GPtrArray *folders = NULL; int i = 0, flags; if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex)) @@ -1801,12 +1803,22 @@ create_folder (CamelStore *store, const char *parent_name, } /* check if the parent allows inferiors */ - + + /* FIXME: use storesummary directly */ + parent_real = camel_imap_store_summary_full_from_path(imap_store->summary, parent_name); + if (parent_real == NULL) { + camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE, + _("Unknown parent folder: %s"), parent_name); + return NULL; + } + need_convert = FALSE; - response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %F", - parent_name); - if (!response) /* whoa, this is bad */ + response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %S", + parent_real); + if (!response) /* whoa, this is bad */ { + g_free(parent_real); return NULL; + } /* FIXME: does not handle unexpected circumstances very well */ for (i = 0; i < response->untagged->len; i++) { @@ -1833,6 +1845,7 @@ create_folder (CamelStore *store, const char *parent_name, if (get_folder_status (imap_store, parent_name, "MESSAGES")) { camel_exception_set (ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE, _("The parent folder is not allowed to contain subfolders")); + g_free(parent_real); return NULL; } @@ -1844,91 +1857,53 @@ create_folder (CamelStore *store, const char *parent_name, } /* add the dirsep to the end of parent_name */ - name = g_strdup_printf ("%s%c", parent_name, imap_store->dir_sep); - response = camel_imap_command (imap_store, NULL, ex, "CREATE %F", + name = g_strdup_printf ("%s%c", parent_real, imap_store->dir_sep); + response = camel_imap_command (imap_store, NULL, ex, "CREATE %S", name); g_free (name); - if (!response) + if (!response) { + g_free(parent_real); return NULL; - else + } else camel_imap_response_free (imap_store, response); + + root = imap_build_folder_info(imap_store, parent_name); } /* ok now we can create the folder */ - - full_name = imap_concat (imap_store, parent_name, folder_name); - response = camel_imap_command (imap_store, NULL, ex, "CREATE %F", - full_name); - g_free (full_name); + real_name = camel_imap_store_summary_path_to_full(imap_store->summary, folder_name, store->dir_sep); + full_name = imap_concat (imap_store, parent_real, real_name); + g_free(real_name); + response = camel_imap_command (imap_store, NULL, ex, "CREATE %S", full_name); if (response) { - CamelFolderInfo *parent, *fi; - + CamelImapStoreInfo *si; + CamelFolderInfo *fi; + camel_imap_response_free (imap_store, response); - - /* We have to do this in case we are creating a - recursive directory structure */ - i = 0; - pathnames = imap_parse_folder_name (imap_store, folder_name); - full_name = imap_concat (imap_store, parent_name, pathnames[i]); - g_free (pathnames[i]); - - folders = g_ptr_array_new (); - - get_folders_online (imap_store, full_name, folders, FALSE, ex); - g_free (full_name); - if (camel_exception_is_set (&internal_ex)) { - camel_exception_xfer (&internal_ex, ex); - goto exception; - } - - root = parent = folders->pdata[i]; - - for (i = 1; parent && pathnames[i]; i++) { - full_name = imap_concat (imap_store, parent_name, pathnames[i]); - g_free (pathnames[i]); - - get_folders_online (imap_store, full_name, folders, FALSE, &internal_ex); - if (camel_exception_is_set (&internal_ex)) { - camel_exception_xfer (&internal_ex, ex); - goto exception; - } - g_free (full_name); - - if (folders->len != i + 1) - break; - - fi = folders->pdata[i]; - camel_folder_info_build_path (fi, imap_store->dir_sep); - parent->child = fi; - fi->parent = parent; - parent = fi; + + si = camel_imap_store_summary_add_from_full(imap_store->summary, full_name, store->dir_sep); + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); + fi = imap_build_folder_info(imap_store, camel_store_info_path(imap_store->summary, si)); + if (root) { + root->child = fi; + fi->parent = root; + } else { + root = fi; } - - camel_folder_info_build_path(root, imap_store->dir_sep); camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root); - - g_free (pathnames); - - g_ptr_array_free (folders, TRUE); + } else if (root) { + /* need to re-recreate the folder we just deleted */ + camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root); + camel_folder_info_free(root); + root = NULL; } + + g_free (full_name); + g_free(parent_real); return root; - - exception: - - for (/* i is already set */; pathnames && pathnames[i]; i++) - g_free (pathnames[i]); - g_free (pathnames); - - if (folders) { - for (i = 0; i < folders->len; i++) - camel_folder_info_free (folders->pdata[i]); - g_ptr_array_free (folders, TRUE); - } - - return NULL; } static CamelFolderInfo * @@ -1936,32 +1911,45 @@ parse_list_response_as_folder_info (CamelImapStore *imap_store, const char *response) { CamelFolderInfo *fi; - int flags; - char sep, *dir, *name = NULL; + int flags, i; + char sep, *dir, *name = NULL, *path; CamelURL *url; if (!imap_parse_list_response (imap_store, response, &flags, &sep, &dir)) return NULL; + + /* FIXME: should use imap_build_folder_info, note the differences with param setting tho */ + path = camel_utf7_utf8(dir); + + /* hack: pokes in value from any list response */ + camel_imap_store_summary_add_from_full(imap_store->summary, dir, sep?sep:'/'); - if (sep) { - name = strrchr (dir, sep); - if (name && !*++name) { - g_free (dir); + if (sep && (name = strrchr(path, sep))) { + if (!*++name) { + g_free(dir); + g_free(path); return NULL; } - } + } else + name = path; fi = g_new0 (CamelFolderInfo, 1); fi->flags = flags; - fi->full_name = dir; - if (sep && name) - fi->name = g_strdup (name); - else - fi->name = g_strdup (dir); - + /*fi->full_name = dir;*/ + fi->name = g_strdup(name); + fi->path = g_strdup_printf("/%s", path); + + if (sep && sep != '/') { + for (i=0;fi->path[i];i++) + if (fi->path[i] == sep) + fi->path[i] = '/'; + } + fi->full_name = g_strdup(fi->path+1); + url = camel_url_new (imap_store->base_url, NULL); g_free (url->path); - url->path = g_strdup_printf ("/%s", dir); + url->path = g_strdup_printf ("/%s", fi->full_name); + if (flags & CAMEL_FOLDER_NOSELECT || fi->name[0] == 0) camel_url_set_param (url, "noselect", "yes"); fi->url = camel_url_to_string (url, 0); @@ -1974,43 +1962,40 @@ parse_list_response_as_folder_info (CamelImapStore *imap_store, return fi; } -static void -copy_folder_name (gpointer name, gpointer key, gpointer array) -{ - g_ptr_array_add (array, name); -} - /* this is used when lsub doesn't provide very useful information */ static GPtrArray * get_subscribed_folders (CamelImapStore *imap_store, const char *top, CamelException *ex) { GPtrArray *names, *folders; + int i, toplen = strlen (top); + CamelStoreInfo *si; CamelImapResponse *response; CamelFolderInfo *fi; char *result; - int i, toplen = strlen (top); - + folders = g_ptr_array_new (); names = g_ptr_array_new (); - g_hash_table_foreach (imap_store->subscribed_folders, - copy_folder_name, names); + for (i=0;(si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i));i++) { + if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) + g_ptr_array_add(names, (char *)camel_imap_store_info_full_name(imap_store->summary, si)); + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } if (names->len == 0) g_ptr_array_add (names, "INBOX"); - + for (i = 0; i < names->len; i++) { response = camel_imap_command (imap_store, NULL, ex, - "LIST \"\" %F", + "LIST \"\" %S", names->pdata[i]); if (!response) break; result = camel_imap_response_extract (imap_store, response, "LIST", NULL); if (!result) { - g_hash_table_remove (imap_store->subscribed_folders, - names->pdata[i]); - g_free (names->pdata[i]); - g_ptr_array_remove_index_fast (names, i--); + camel_store_summary_remove_path((CamelStoreSummary *)imap_store->summary, names->pdata[i]); + g_ptr_array_remove_index_fast (names, i); + i--; continue; } @@ -2031,6 +2016,31 @@ get_subscribed_folders (CamelImapStore *imap_store, const char *top, CamelExcept return folders; } +static int imap_match_pattern(char dir_sep, const char *pattern, const char *name) +{ + char p, n; + + p = *pattern++; + n = *name++; + while (n && p) { + if (n == p) { + p = *pattern++; + n = *name++; + } else if (p == '%') { + if (n != dir_sep) { + n = *name++; + } else { + p = *pattern++; + } + } else if (p == '*') { + return TRUE; + } else + return FALSE; + } + + return n == 0 && (p == '%' || p == 0); +} + static void get_folders_online (CamelImapStore *imap_store, const char *pattern, GPtrArray *folders, gboolean lsub, CamelException *ex) @@ -2038,21 +2048,56 @@ get_folders_online (CamelImapStore *imap_store, const char *pattern, CamelImapResponse *response; CamelFolderInfo *fi; char *list; - int i; - + int i, count; + GHashTable *present; + CamelStoreInfo *si; + response = camel_imap_command (imap_store, NULL, ex, - "%s \"\" %F", lsub ? "LSUB" : "LIST", + "%s \"\" %S", lsub ? "LSUB" : "LIST", pattern); if (!response) return; - + + present = g_hash_table_new(g_str_hash, g_str_equal); 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) - g_ptr_array_add (folders, fi); + if (fi) { + g_ptr_array_add(folders, fi); + g_hash_table_insert(present, fi->full_name, fi); + } } camel_imap_response_free (imap_store, response); + + /* update our summary to match the server */ + count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary); + for (i=0;i<count;i++) { + si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i); + if (si == NULL) + continue; + + if (imap_match_pattern(((CamelStore *)imap_store)->dir_sep, pattern, camel_imap_store_info_full_name(imap_store->summary, si))) { + if (g_hash_table_lookup(present, camel_store_info_path(imap_store->summary, si)) != NULL) { + if (lsub && (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) { + si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + camel_store_summary_touch((CamelStoreSummary *)imap_store->summary); + } + } else { + if (lsub) { + if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) { + si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + camel_store_summary_touch((CamelStoreSummary *)imap_store->summary); + } + } else { + camel_store_summary_remove((CamelStoreSummary *)imap_store->summary, si); + count--; + i--; + } + } + } + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } + g_hash_table_destroy(present); } #if 1 @@ -2072,7 +2117,7 @@ dumpfi(CamelFolderInfo *fi) } while (fi) { - printf("%-40s %-30s %*s\n", fi->path, fi->full_name, depth*2+strlen(fi->name), fi->name); + printf("%-40s %-30s %*s\n", fi->path, fi->full_name, depth*2+strlen(fi->url), fi->url); if (fi->child) dumpfi(fi->child); fi = fi->sibling; @@ -2179,13 +2224,21 @@ get_folders(CamelStore *store, const char *top, guint32 flags, CamelException *e infos = g_hash_table_new(folder_hash, folder_eq); /* get starting point & strip trailing '/' */ - if (top[0] == 0 && imap_store->namespace) - top = imap_store->namespace; - i = strlen(top)-1; - name = alloca(i+2); - strcpy(name, top); - while (i>0 && name[i] == store->dir_sep) - name[i--] = 0; + if (top[0] == 0) { + if (imap_store->namespace) { + top = imap_store->namespace; + i = strlen(top)-1; + name = g_malloc(i+2); + strcpy(name, top); + while (i>0 && name[i] == store->dir_sep) + name[i--] = 0; + } else + name = g_strdup(""); + } else { + name = camel_imap_store_summary_full_from_path(imap_store->summary, top); + if (name == NULL) + name = camel_imap_store_summary_path_to_full(imap_store->summary, top, store->dir_sep); + } d(printf("\n\nList '%s' %s\n", name, flags&CAMEL_STORE_FOLDER_INFO_RECURSIVE?"RECURSIVE":"NON-RECURSIVE")); @@ -2194,13 +2247,18 @@ get_folders(CamelStore *store, const char *top, guint32 flags, CamelException *e /* first get working list of names */ get_folders_online (imap_store, name[0]?name:"%", folders, flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, ex); + if (camel_exception_is_set(ex)) + goto fail; for (i=0; i<folders->len && !haveinbox; i++) { fi = folders->pdata[i]; haveinbox = (strcasecmp(fi->full_name, "INBOX")) == 0; } - if (!haveinbox && top == imap_store->namespace) + if (!haveinbox && top == imap_store->namespace) { get_folders_online(imap_store, "INBOX", folders, flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, ex); + if (camel_exception_is_set(ex)) + goto fail; + } for (i=0; i<folders->len; i++) p = g_slist_prepend(p, folders->pdata[i]); @@ -2230,11 +2288,13 @@ get_folders(CamelStore *store, const char *top, guint32 flags, CamelException *e /* Otherwise, if this has (or might have) children, scan it */ else if ( (fi->flags & (CAMEL_IMAP_FOLDER_NOCHILDREN|CAMEL_FOLDER_NOINFERIORS)) == 0 || (fi->flags & CAMEL_FOLDER_CHILDREN) != 0) { - char *n; - - n = imap_concat(imap_store, fi->full_name, "%"); + char *n, *real; + + real = camel_imap_store_summary_full_from_path(imap_store->summary, fi->full_name); + n = imap_concat(imap_store, real?real:fi->full_name, "%"); get_folders_online(imap_store, n, folders, flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, ex); g_free(n); + g_free(real); if (folders->len > 0) fi->flags |= CAMEL_FOLDER_CHILDREN; @@ -2259,8 +2319,16 @@ get_folders(CamelStore *store, const char *top, guint32 flags, CamelException *e g_ptr_array_free(folders, TRUE); g_hash_table_destroy(infos); + g_free(name); return folders_out; +fail: + g_ptr_array_free(folders, TRUE); + g_ptr_array_free(folders_out, TRUE); + g_hash_table_destroy(infos); + g_free(name); + + return NULL; } static CamelFolderInfo * @@ -2280,14 +2348,18 @@ get_folder_info_online (CamelStore *store, const char *top, guint32 flags, Camel else folders = get_folders(store, top, flags, ex); + if (folders == NULL) + return NULL; + /* note the weird top stuff, it is so a namespace based list "" is properly tree-ised */ - tree = camel_folder_info_build(folders, top[0] == 0 && imap_store->namespace?"":top, imap_store->dir_sep, TRUE); + tree = camel_folder_info_build(folders, top[0] == 0 && imap_store->namespace?"":top, '/', TRUE); g_ptr_array_free(folders, TRUE); if (!(flags & CAMEL_STORE_FOLDER_INFO_FAST)) get_folder_counts(imap_store, tree, ex); dumpfi(tree); + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); return tree; } @@ -2298,7 +2370,7 @@ get_one_folder_offline (const char *physical_path, const char *path, gpointer da GPtrArray *folders = data; CamelImapStore *imap_store = folders->pdata[0]; CamelFolderInfo *fi; - CamelURL *url; + CamelStoreInfo *si; if (*path != '/') return TRUE; @@ -2309,26 +2381,16 @@ get_one_folder_offline (const char *physical_path, const char *path, gpointer da * not a folder we're explicitly interested in. */ - if (g_hash_table_lookup (imap_store->subscribed_folders, path + 1) == 0) - return TRUE; - - fi = g_new0 (CamelFolderInfo, 1); - fi->full_name = g_strdup (path+1); - fi->name = strrchr (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); - - url = camel_url_new(imap_store->base_url, NULL); - camel_url_set_path(url, path); - fi->url = camel_url_to_string(url, 0); - camel_url_free(url); - - /* FIXME: check summary */ - fi->unread_message_count = -1; + si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, path+1); + if (si) { + if ((((CamelStore *)imap_store)->flags & CAMEL_STORE_SUBSCRIPTIONS) == 0 + || si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) { + fi = imap_build_folder_info(imap_store, path+1); + g_ptr_array_add (folders, fi); + } + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } - g_ptr_array_add (folders, fi); return TRUE; } @@ -2363,8 +2425,7 @@ get_folder_info_offline (CamelStore *store, const char *top, fi = NULL; } else { g_ptr_array_remove_index_fast (folders, 0); - fi = camel_folder_info_build (folders, "", - imap_store->dir_sep, TRUE); + fi = camel_folder_info_build (folders, "", '/', TRUE); } g_free(storage_path); @@ -2376,13 +2437,19 @@ static gboolean folder_subscribed (CamelStore *store, const char *folder_name) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); + CamelStoreInfo *si; + int truth = FALSE; - g_return_val_if_fail (imap_store->subscribed_folders != NULL, FALSE); + si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name); + if (si) { + truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0; + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } - return g_hash_table_lookup (imap_store->subscribed_folders, - folder_name) != NULL; + return truth; } +/* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */ static void subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) @@ -2390,9 +2457,8 @@ subscribe_folder (CamelStore *store, const char *folder_name, CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; CamelFolderInfo *fi; - const char *name; - CamelURL *url; - + CamelStoreInfo *si; + if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex)) return; if (!camel_imap_store_connected (imap_store, ex)) @@ -2404,8 +2470,15 @@ subscribe_folder (CamelStore *store, const char *folder_name, return; camel_imap_response_free (imap_store, response); - g_hash_table_insert (imap_store->subscribed_folders, - g_strdup (folder_name), GUINT_TO_POINTER (1)); + si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name); + if (si) { + if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) { + si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + camel_store_summary_touch((CamelStoreSummary *)imap_store->summary); + camel_store_summary_save((CamelStoreSummary *)imap_store->summary); + } + camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si); + } if (imap_store->renaming) { /* we don't need to emit a "folder_subscribed" signal @@ -2413,26 +2486,8 @@ subscribe_folder (CamelStore *store, const char *folder_name, are done here... */ return; } - - name = strrchr (folder_name, imap_store->dir_sep); - if (name) - name++; - else - name = folder_name; - - url = camel_url_new (imap_store->base_url, NULL); - g_free (url->path); - url->path = g_strdup_printf ("/%s", folder_name); - - fi = g_new0 (CamelFolderInfo, 1); - fi->full_name = g_strdup (folder_name); - fi->name = g_strdup (name); - fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); - fi->unread_message_count = -1; - camel_folder_info_build_path (fi, imap_store->dir_sep); - - camel_url_free (url); - + + fi = imap_build_folder_info(imap_store, folder_name); camel_object_trigger_event (CAMEL_OBJECT (store), "folder_subscribed", fi); camel_folder_info_free (fi); } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index 499c282649..38df1880a0 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -102,6 +102,8 @@ struct _CamelImapStore { CamelStream *istream; CamelStream *ostream; + + struct _CamelImapStoreSummary *summary; /* Information about the command channel / connection status */ gboolean connected; @@ -113,7 +115,7 @@ struct _CamelImapStore { CamelImapServerLevel server_level; guint32 capabilities, parameters; char *namespace, dir_sep, *base_url, *storage_path; - GHashTable *authtypes, *subscribed_folders; + GHashTable *authtypes; gboolean renaming; diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c index 15bf2540b3..fc507398db 100644 --- a/camel/providers/imap/camel-imap-utils.c +++ b/camel/providers/imap/camel-imap-utils.c @@ -20,11 +20,19 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include <ctype.h> #include <stdio.h> #include <string.h> #include <time.h> -#include <iconv.h> +#include <errno.h> + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif #include "camel-imap-utils.h" #include "camel-imap-summary.h" @@ -363,7 +371,9 @@ imap_parse_list_response (CamelImapStore *store, const char *buf, int *flags, ch astring = imap_parse_astring (&word, &len); if (!astring) return FALSE; - + + *folder = astring; +#if 0 mailbox = imap_mailbox_decode (astring, strlen (astring)); g_free (astring); if (!mailbox) @@ -385,6 +395,7 @@ imap_parse_list_response (CamelImapStore *store, const char *buf, int *flags, ch *flags &= ~CAMEL_FOLDER_NOSELECT; *folder = mailbox; +#endif } return TRUE; |