diff options
author | Dan Winship <danw@src.gnome.org> | 2001-03-16 04:50:59 +0800 |
---|---|---|
committer | Dan Winship <danw@src.gnome.org> | 2001-03-16 04:50:59 +0800 |
commit | 1f9d06c2aac1805bbd3922991d8332b44f16ad3e (patch) | |
tree | 0b363d1cb7795c0c20f4f734541b70291d09c213 /camel/providers/imap/camel-imap-message-cache.c | |
parent | 892997d8c0f0cc6105ca8b0a8a369b97aab45dcb (diff) | |
download | gsoc2013-evolution-1f9d06c2aac1805bbd3922991d8332b44f16ad3e.tar.gz gsoc2013-evolution-1f9d06c2aac1805bbd3922991d8332b44f16ad3e.tar.zst gsoc2013-evolution-1f9d06c2aac1805bbd3922991d8332b44f16ad3e.zip |
First batch of disconnected IMAP-related stuff. This adds local
caching of message parts, but NOT any actual disconnected support.
(But it should speed up IMAP use.)
* providers/imap/camel-imap-message-cache.c: New class for caching
message data to disk, and removing it when it's no longer
relevant. Will eventually also support merging message parts
together to save on files. Or maybe using a db instead of files?
* providers/imap/camel-imap-private.h: Add a cache_lock to
CamelImapFolderPrivate. This lock must be recursive, so make both
locks EMutexes rather than GMutex.
* providers/imap/camel-imap-folder.c (parse_fetch_response): "The
only FETCH response parser you need!" Replaces the various
almost-correct bits of code formerly scattered throughout this
file with a single fully-correct function that can handle any
FETCH response at any time, so we don't get confused by seeing a
flags update when we were only expecting a message body, etc.
(camel_imap_folder_fetch_data): FETCH a message body part either
from the cache or the server
(camel_imap_folder_changed): Remove expunged messages from the
message cache.
(camel_imap_folder_new): Change to take a directory instead of a
summary file name. Create a CamelImapMessageCache for the folder.
(imap_finalize): Unref the message cache.
(camel_imap_folder_selected, imap_rescan, get_content,
get_message, imap_get_message, imap_update_summary): Redone a
bunch to use parse_fetch_data, CamelImapMessageCache, etc.
* providers/imap/camel-imap-store.c (get_folder): Pass directory
name to camel_imap_folder_new, not summary filename. Use
e_path_to_physical to generate a path with /subfolders/ inserted
between directory components.
* providers/imap/camel-imap-wrapper.c (camel_imap_wrapper_new):
Call camel_imap_folder_fetch_data (with cache_only TRUE) and if
the data is cached, return an online datawrapper rather than an
offline one.
(write_to_stream): Use camel_imap_folder_fetch_data (with
cache_only FALSE) here too
* providers/imap/camel-imap-utils.c (imap_skip_list): Renamed from
skip_list and made non-static.
svn path=/trunk/; revision=8743
Diffstat (limited to 'camel/providers/imap/camel-imap-message-cache.c')
-rw-r--r-- | camel/providers/imap/camel-imap-message-cache.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/camel/providers/imap/camel-imap-message-cache.c b/camel/providers/imap/camel-imap-message-cache.c new file mode 100644 index 0000000000..0ac90f7062 --- /dev/null +++ b/camel/providers/imap/camel-imap-message-cache.c @@ -0,0 +1,277 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-imap-message-cache.c: Class for an IMAP message cache */ + +/* + * Author: + * Dan Winship <danw@ximian.com> + * + * Copyright (C) 2001 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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 + */ + +#include <config.h> + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> + +#include "camel-imap-message-cache.h" +#include "camel-exception.h" +#include "camel-stream-fs.h" + +static void finalize (CamelImapMessageCache *cache); +static void stream_finalize (CamelObject *stream, gpointer event_data, gpointer user_data); + + +CamelType +camel_imap_message_cache_get_type (void) +{ + static CamelType camel_imap_message_cache_type = CAMEL_INVALID_TYPE; + + if (camel_imap_message_cache_type == CAMEL_INVALID_TYPE) { + camel_imap_message_cache_type = camel_type_register ( + CAMEL_OBJECT_TYPE, "CamelImapMessageCache", + sizeof (CamelImapMessageCache), + sizeof (CamelImapMessageCacheClass), + NULL, + NULL, + NULL, + (CamelObjectFinalizeFunc) finalize); + } + + return camel_imap_message_cache_type; +} + +static void +free_part (gpointer key, gpointer value, gpointer data) +{ + if (value) { + if (strchr (key, '.')) { + camel_object_unhook_event (value, "finalize", + stream_finalize, data); + camel_object_unref (value); + } else + g_ptr_array_free (value, TRUE); + } + g_free (key); +} + +static void +finalize (CamelImapMessageCache *cache) +{ + if (cache->path) + g_free (cache->path); + if (cache->parts) { + g_hash_table_foreach (cache->parts, free_part, cache); + g_hash_table_destroy (cache->parts); + } + if (cache->cached) + g_hash_table_destroy (cache->cached); +} + +static void +cache_put (CamelImapMessageCache *cache, const char *uid, const char *key, + CamelStream *stream) +{ + char *hash_key; + GPtrArray *subparts; + gpointer old_key, old_value; + + hash_key = g_strdup (key); + subparts = g_hash_table_lookup (cache->parts, uid); + if (!subparts) { + subparts = g_ptr_array_new (); + g_hash_table_insert (cache->parts, g_strdup (uid), subparts); + } else if (g_hash_table_lookup_extended (cache->parts, hash_key, + &old_key, &old_value)) + g_ptr_array_remove (subparts, old_key); + + g_ptr_array_add (subparts, hash_key); + g_hash_table_insert (cache->parts, hash_key, stream); + g_hash_table_insert (cache->cached, stream, hash_key); + + if (stream) { + camel_object_hook_event (CAMEL_OBJECT (stream), "finalize", + stream_finalize, cache); + } +} + +CamelImapMessageCache * +camel_imap_message_cache_new (const char *path, CamelFolderSummary *summary, + CamelException *ex) +{ + CamelImapMessageCache *cache; + DIR *dir; + struct dirent *d; + char *uid, *p; + GPtrArray *deletes; + CamelMessageInfo *info; + + dir = opendir (path); + if (!dir) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not open cache directory: %s"), + g_strerror (errno)); + return NULL; + } + + cache = (CamelImapMessageCache *)camel_object_new (CAMEL_IMAP_MESSAGE_CACHE_TYPE); + cache->path = g_strdup (path); + + cache->parts = g_hash_table_new (g_str_hash, g_str_equal); + cache->cached = g_hash_table_new (NULL, NULL); + deletes = g_ptr_array_new (); + while ((d = readdir (dir))) { + if (!isdigit (d->d_name[0])) + continue; + + p = strchr (d->d_name, '.'); + if (p) + uid = g_strndup (d->d_name, p - d->d_name); + else + uid = g_strdup (d->d_name); + + info = camel_folder_summary_uid (summary, uid); + if (info) { + camel_folder_summary_info_free (summary, info); + cache_put (cache, uid, d->d_name, NULL); + } else + g_ptr_array_add (deletes, g_strdup_printf ("%s/%s", cache->path, d->d_name)); + g_free (uid); + } + closedir (dir); + + while (deletes->len) { + unlink (deletes->pdata[0]); + g_free (deletes->pdata[0]); + g_ptr_array_remove_index_fast (deletes, 0); + } + g_ptr_array_free (deletes, TRUE); + + if (camel_exception_is_set (ex)) { + camel_object_unref (CAMEL_OBJECT (cache)); + return NULL; + } + + return cache; +} + + +static void +stream_finalize (CamelObject *stream, gpointer event_data, gpointer user_data) +{ + CamelImapMessageCache *cache = user_data; + char *key; + + key = g_hash_table_lookup (cache->cached, stream); + if (!key) + return; + g_hash_table_remove (cache->cached, stream); + g_hash_table_insert (cache->parts, key, NULL); +} + +CamelStream * +camel_imap_message_cache_insert (CamelImapMessageCache *cache, const char *uid, + const char *part_spec, const char *data, + int len) +{ + char *path, *key; + int fd, status; + CamelStream *stream; + + path = g_strdup_printf ("%s/%s.%s", cache->path, uid, part_spec); + key = strrchr (path, '/') + 1; + stream = g_hash_table_lookup (cache->parts, key); + if (stream) + camel_object_unref (CAMEL_OBJECT (stream)); + + fd = open (path, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) { + g_free (path); + return NULL; + } + + stream = camel_stream_fs_new_with_fd (fd); + status = camel_stream_write (stream, data, len); + camel_stream_reset (stream); + + if (status == -1) { + unlink (path); + g_free (path); + camel_object_unref (CAMEL_OBJECT (stream)); + return NULL; + } + + cache_put (cache, uid, key, stream); + g_free (path); + + return stream; +} + +CamelStream * +camel_imap_message_cache_get (CamelImapMessageCache *cache, const char *uid, + const char *part_spec) +{ + CamelStream *stream; + char *path, *key; + + path = g_strdup_printf ("%s/%s.%s", cache->path, uid, part_spec); + key = strrchr (path, '/'); + stream = g_hash_table_lookup (cache->parts, key); + if (stream) { + camel_object_ref (CAMEL_OBJECT (stream)); + return stream; + } + + stream = camel_stream_fs_new_with_name (path, O_RDONLY, 0); + if (stream) + cache_put (cache, uid, key, stream); + g_free (path); + + return stream; +} + +void +camel_imap_message_cache_remove (CamelImapMessageCache *cache, const char *uid) +{ + GPtrArray *subparts; + char *key, *path; + CamelObject *stream; + int i; + + subparts = g_hash_table_lookup (cache->parts, uid); + if (!subparts) + return; + for (i = 0; i < subparts->len; i++) { + key = subparts->pdata[i]; + path = g_strdup_printf ("%s/%s", cache->path, key); + unlink (path); + stream = g_hash_table_lookup (cache->parts, key); + if (stream) { + camel_object_unhook_event (stream, "finalize", + stream_finalize, cache); + camel_object_unref (stream); + g_hash_table_remove (cache->cached, stream); + } + g_hash_table_remove (cache->parts, key); + g_free (key); + } + g_hash_table_remove (cache->parts, uid); + g_ptr_array_free (subparts, TRUE); +} + |