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 | |
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')
-rw-r--r-- | camel/ChangeLog | 47 | ||||
-rw-r--r-- | camel/providers/imap/Makefile.am | 2 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-folder.c | 486 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-folder.h | 9 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-message-cache.c | 277 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-message-cache.h | 85 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-private.h | 7 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.c | 19 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-types.h | 11 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.c | 8 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-utils.h | 2 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-wrapper.c | 96 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-wrapper.h | 5 |
13 files changed, 786 insertions, 268 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 81ef2d5295..ff5caf930d 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,50 @@ +2001-03-15 Dan Winship <danw@ximian.com> + + 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. + 2001-03-15 Jeffrey Stedfast <fejj@ximian.com> * camel-tcp-stream-ssl.h: Uninclude prnetdb.h since it's not diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am index d38302004c..d29a1677af 100644 --- a/camel/providers/imap/Makefile.am +++ b/camel/providers/imap/Makefile.am @@ -22,6 +22,7 @@ INCLUDES = -I.. \ libcamelimap_la_SOURCES = \ camel-imap-command.c \ camel-imap-folder.c \ + camel-imap-message-cache.c \ camel-imap-provider.c \ camel-imap-search.c \ camel-imap-store.c \ @@ -32,6 +33,7 @@ libcamelimap_la_SOURCES = \ libcamelimapinclude_HEADERS = \ camel-imap-command.h \ camel-imap-folder.h \ + camel-imap-message-cache.h \ camel-imap-search.h \ camel-imap-store.h \ camel-imap-summary.h \ diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index cf91bceb12..2a99df8149 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -39,6 +39,7 @@ #include "camel-imap-folder.h" #include "camel-imap-command.h" +#include "camel-imap-message-cache.h" #include "camel-imap-search.h" #include "camel-imap-store.h" #include "camel-imap-summary.h" @@ -46,7 +47,6 @@ #include "camel-imap-wrapper.h" #include "string-utils.h" #include "camel-stream.h" -#include "camel-stream-fs.h" #include "camel-stream-mem.h" #include "camel-stream-buffer.h" #include "camel-data-wrapper.h" @@ -92,6 +92,8 @@ static void imap_update_summary (CamelFolder *folder, int first, int last, static GPtrArray *imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex); static void imap_search_free (CamelFolder *folder, GPtrArray *uids); +GData *parse_fetch_response (CamelImapFolder *imap_folder, char *msg_att); + static void camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class) { @@ -127,7 +129,8 @@ camel_imap_folder_init (gpointer object, gpointer klass) imap_folder->priv = g_malloc0(sizeof(*imap_folder->priv)); #ifdef ENABLE_THREADS - imap_folder->priv->search_lock = g_mutex_new(); + imap_folder->priv->search_lock = e_mutex_new(E_MUTEX_SIMPLE); + imap_folder->priv->cache_lock = e_mutex_new(E_MUTEX_REC); #endif } @@ -152,13 +155,14 @@ camel_imap_folder_get_type (void) CamelFolder * camel_imap_folder_new (CamelStore *parent, const char *folder_name, - const char *short_name, const char *summary_file, + const char *short_name, const char *folder_dir, CamelException *ex) { CamelImapStore *imap_store = CAMEL_IMAP_STORE (parent); CamelFolder *folder = CAMEL_FOLDER (camel_object_new (camel_imap_folder_get_type ())); + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelImapResponse *response; - char *resp; + char *resp, *summary_file; guint32 validity = 0; int i, exists = 0; @@ -192,7 +196,9 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, } camel_imap_response_free (response); + summary_file = g_strdup_printf ("%s/summary", folder_dir); folder->summary = camel_imap_summary_new (summary_file, validity); + g_free (summary_file); if (!folder->summary) { camel_object_unref (CAMEL_OBJECT (folder)); camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, @@ -201,6 +207,12 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, return NULL; } + imap_folder->cache = camel_imap_message_cache_new (folder_dir, folder->summary, ex); + if (!imap_folder->cache) { + camel_object_unref (CAMEL_OBJECT (folder)); + return NULL; + } + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); imap_rescan (folder, exists, ex); CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); @@ -217,8 +229,10 @@ void camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response, CamelException *ex) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); unsigned long exists = 0, val, uid; CamelMessageInfo *info; + GData *fetch_data; int i, count; char *resp; @@ -259,13 +273,15 @@ camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response, for (i = 0; i < response->untagged->len; i++) { resp = response->untagged->pdata[i]; val = strtoul (resp + 2, &resp, 10); - if (val != count || g_strncasecmp (resp, " FETCH (", 8) != 0) - continue; - resp = e_strstrcase (resp, "UID "); - if (!resp) - continue; - uid = strtoul (resp + 4, NULL, 10); - break; + if (val == count && !g_strncasecmp (resp, " FETCH (", 8)) + break; + } + + if (i < response->untagged->len) { + fetch_data = parse_fetch_response (imap_folder, resp + 7); + uid = strtoul (g_datalist_get_data (&fetch_data, "UID"), + NULL, 10); + g_datalist_clear (&fetch_data); } camel_imap_response_free (response); @@ -293,10 +309,13 @@ imap_finalize (CamelObject *object) CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (object); if (imap_folder->search) - camel_object_unref ((CamelObject *)imap_folder->search); + camel_object_unref (CAMEL_OBJECT (imap_folder->search)); + if (imap_folder->cache) + camel_object_unref (CAMEL_OBJECT (imap_folder->cache)); #ifdef ENABLE_THREADS - g_mutex_free(imap_folder->priv->search_lock); + e_mutex_destroy(imap_folder->priv->search_lock); + e_mutex_destroy(imap_folder->priv->cache_lock); #endif g_free(imap_folder->priv); } @@ -313,18 +332,19 @@ imap_refresh_info (CamelFolder *folder, CamelException *ex) static void imap_rescan (CamelFolder *folder, int exists, CamelException *ex) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapResponse *response; struct { char *uid; guint32 flags; } *new = NULL; - char *resp, *p, *flags; - const char *uid; + char *resp; int i, j, seq, summary_len; CamelMessageInfo *info; CamelImapMessageInfo *iinfo; GArray *removed; + GData *fetch_data; camel_operation_start(NULL, _("Scanning IMAP folder")); @@ -341,21 +361,14 @@ imap_rescan (CamelFolder *folder, int exists, CamelException *ex) resp = response->untagged->pdata[i]; seq = strtoul (resp + 2, &resp, 10); - if (g_strncasecmp (resp, " FETCH ", 7) != 0) + if (g_strncasecmp (resp, " FETCH (", 8) != 0) continue; - uid = e_strstrcase (resp, "UID "); - if (uid) { - uid += 4; - strtoul (uid, &p, 10); - new[seq - 1].uid = g_strndup (uid, p - uid); - } - - flags = e_strstrcase (resp, "FLAGS "); - if (flags) { - flags += 6; - new[seq - 1].flags = imap_parse_flag_list (&flags); - } + fetch_data = parse_fetch_response (imap_folder, resp + 7); + new[seq - 1].uid = g_strdup (g_datalist_get_data (&fetch_data, "UID")); + new[seq - 1].flags = GPOINTER_TO_UINT (g_datalist_get_data (&fetch_data, "FLAGS")); + g_datalist_clear (&fetch_data); + g_ptr_array_remove_index_fast (response->untagged, i--); } camel_imap_response_free (response); } @@ -767,83 +780,8 @@ imap_search_free (CamelFolder *folder, GPtrArray *uids) CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock); } -/* parse a header response (starting after the first ' ' after - * *@headers_p) and construct a content-free CamelMedium from it. - */ -static CamelMedium * -parse_headers (char **headers_p, CamelType medium_type) -{ - CamelMedium *medium; - CamelStream *stream; - char *headers; - int len; - - *headers_p = strchr (*headers_p, ' '); - if (!*headers_p) - return FALSE; - (*headers_p)++; - - headers = imap_parse_nstring (headers_p, &len); - if (!headers) - return FALSE; - stream = camel_stream_mem_new_with_buffer (headers, len); - g_free (headers); - - medium = CAMEL_MEDIUM (camel_object_new (medium_type)); - camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (medium), stream); - camel_object_unref (CAMEL_OBJECT (stream)); - - return medium; -} - -static CamelMedium * -fetch_medium (CamelFolder *folder, const char *uid, const char *section_text, - CamelType type, CamelException *ex) -{ - CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); - CamelImapResponse *response; - CamelMedium *medium; - char *result, *p; - - CAMEL_IMAP_STORE_LOCK (store, command_lock); - if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) { - response = camel_imap_command (store, folder, ex, - "UID FETCH %s RFC822.PEEK", - uid); - } else { - response = camel_imap_command (store, folder, ex, - "UID FETCH %s BODY.PEEK[%s]", - uid, section_text); - } - CAMEL_IMAP_STORE_UNLOCK (store, command_lock); - if (!response) - return NULL; - - /* FIXME: there could be multiple lines of FETCH response. */ - result = camel_imap_response_extract (response, "FETCH", ex); - if (!result) - return NULL; - - - if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) - p = e_strstrcase (result, "RFC822"); - else - p = e_strstrcase (result, "BODY"); - - if (p) - medium = parse_headers (&p, type); - else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Could not find message body in FETCH " - "response.")); - medium = NULL; - } - g_free (result); - - return medium; -} - -static CamelMimeMessage *get_message (CamelFolder *folder, const char *uid, +static CamelMimeMessage *get_message (CamelImapFolder *imap_folder, + const char *uid, const char *part_specifier, CamelMessageContentInfo *ci, CamelException *ex); @@ -852,17 +790,18 @@ static CamelMimeMessage *get_message (CamelFolder *folder, const char *uid, * of message @uid in @folder. */ static CamelDataWrapper * -get_content (CamelFolder *folder, const char *uid, const char *part_spec, - CamelMimePart *part, CamelMessageContentInfo *ci, - CamelException *ex) +get_content (CamelImapFolder *imap_folder, const char *uid, + const char *part_spec, CamelMimePart *part, + CamelMessageContentInfo *ci, CamelException *ex) { + CamelDataWrapper *content; + CamelStream *stream; char *child_spec; /* There are three cases: multipart, message/rfc822, and "other" */ if (header_content_type_is (ci->type, "multipart", "*")) { CamelMultipart *body_mp; - CamelDataWrapper *content; int speclen, num; body_mp = camel_multipart_new (); @@ -880,18 +819,21 @@ get_content (CamelFolder *folder, const char *uid, const char *part_spec, num = 1; while (ci) { sprintf (child_spec + speclen, "%d.MIME", num++); - part = (CamelMimePart *)fetch_medium (folder, uid, child_spec, CAMEL_MIME_PART_TYPE, ex); - *(strchr (child_spec + speclen, '.')) = '\0'; - if (part) - content = get_content (folder, uid, child_spec, part, ci, ex); - if (!part || !content) { + stream = camel_imap_folder_fetch_data (imap_folder, uid, child_spec, FALSE, ex); + if (stream) { + part = camel_mime_part_new (); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (part), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + *(strchr (child_spec + speclen, '.')) = '\0'; + content = get_content (imap_folder, uid, child_spec, part, ci, ex); + } + if (!stream || !content) { g_free (child_spec); - camel_object_unref (CAMEL_OBJECT (part)); camel_object_unref (CAMEL_OBJECT (body_mp)); return NULL; } - camel_medium_set_content_object (CAMEL_MEDIUM (part), - content); + + camel_medium_set_content_object (CAMEL_MEDIUM (part), content); camel_object_unref (CAMEL_OBJECT (content)); camel_multipart_add_part (body_mp, part); camel_object_unref (CAMEL_OBJECT (part)); @@ -903,37 +845,42 @@ get_content (CamelFolder *folder, const char *uid, const char *part_spec, return (CamelDataWrapper *)body_mp; } else if (header_content_type_is (ci->type, "message", "rfc822")) { return (CamelDataWrapper *) - get_message (folder, uid, part_spec, ci->childs, ex); + get_message (imap_folder, uid, part_spec, ci->childs, ex); } else { - CamelDataWrapper *content; - if (!ci->parent || header_content_type_is (ci->parent->type, "message", "rfc822")) child_spec = g_strdup_printf ("%s%s1", part_spec, *part_spec ? "." : ""); else child_spec = g_strdup (part_spec); - content = camel_imap_wrapper_new (folder, ci->type, uid, child_spec, part); + + content = camel_imap_wrapper_new (imap_folder, ci->type, uid, child_spec, part); g_free (child_spec); return content; } } static CamelMimeMessage * -get_message (CamelFolder *folder, const char *uid, const char *part_spec, - CamelMessageContentInfo *ci, CamelException *ex) +get_message (CamelImapFolder *imap_folder, const char *uid, + const char *part_spec, CamelMessageContentInfo *ci, + CamelException *ex) { - CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapStore *store = CAMEL_IMAP_STORE (CAMEL_FOLDER (imap_folder)->parent_store); CamelDataWrapper *content; CamelMimeMessage *msg; + CamelStream *stream; char *section_text; section_text = g_strdup_printf ("%s%s%s", part_spec, *part_spec ? "." : "", store->server_level >= IMAP_LEVEL_IMAP4REV1 ? "HEADER" : "0"); - msg = (CamelMimeMessage *)fetch_medium (folder, uid, section_text, CAMEL_MIME_MESSAGE_TYPE, ex); + stream = camel_imap_folder_fetch_data (imap_folder, uid, section_text, FALSE, ex); g_free (section_text); - if (!msg) + if (!stream) return NULL; - content = get_content (folder, uid, part_spec, CAMEL_MIME_PART (msg), ci, ex); + msg = camel_mime_message_new (); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + content = get_content (imap_folder, uid, part_spec, CAMEL_MIME_PART (msg), ci, ex); if (!content) { camel_object_unref (CAMEL_OBJECT (msg)); return NULL; @@ -951,8 +898,10 @@ get_message (CamelFolder *folder, const char *uid, const char *part_spec, static CamelMimeMessage * imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelMessageInfo *mi; CamelMimeMessage *msg; + CamelStream *stream; mi = camel_folder_summary_uid (folder->summary, uid); g_return_val_if_fail (mi != NULL, NULL); @@ -960,7 +909,13 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) /* Fetch small messages directly. */ if (mi->size < IMAP_SMALL_BODY_SIZE) { camel_folder_summary_info_free (folder->summary, mi); - return (CamelMimeMessage *)fetch_medium (folder, uid, "", CAMEL_MIME_MESSAGE_TYPE, ex); + stream = camel_imap_folder_fetch_data (imap_folder, uid, "", FALSE, ex); + if (!stream) + return NULL; + msg = camel_mime_message_new (); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + return msg; } /* For larger messages, fetch the structure and build a message @@ -971,7 +926,9 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) if (!mi->content->type) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapResponse *response; - char *result, *p; + GData *fetch_data; + char *body, *found_uid; + int i; CAMEL_IMAP_STORE_LOCK (store, command_lock); response = camel_imap_command (store, folder, ex, @@ -981,19 +938,22 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) camel_folder_summary_info_free (folder->summary, mi); return NULL; } - /* FIXME, wrong */ - result = camel_imap_response_extract (response, "FETCH", ex); - if (!result) { - camel_folder_summary_info_free (folder->summary, mi); - return NULL; - } - p = e_strstrcase (result, "BODY "); - if (p) { - p += 5; - imap_parse_body (&p, folder, mi->content); + for (i = 0, body = NULL; i < response->untagged->len; i++) { + fetch_data = parse_fetch_response (imap_folder, response->untagged->pdata[i]); + found_uid = g_datalist_get_data (&fetch_data, "UID"); + body = g_datalist_get_data (&fetch_data, "BODY"); + if (found_uid && body && !strcmp (found_uid, uid)) + break; + g_datalist_clear (&fetch_data); + body = NULL; } - g_free (result); + + if (body) + imap_parse_body (&body, folder, mi->content); + g_datalist_clear (&fetch_data); + camel_imap_response_free (response); + if (!mi->content->type) { camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, _("Could not find message body in FETCH response.")); @@ -1002,7 +962,7 @@ imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex) } } - msg = get_message (folder, uid, "", mi->content, ex); + msg = get_message (imap_folder, uid, "", mi->content, ex); camel_folder_summary_info_free (folder->summary, mi); return msg; @@ -1021,15 +981,16 @@ static void imap_update_summary (CamelFolder *folder, int first, int last, CamelFolderChangeInfo *changes, CamelException *ex) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); CamelImapResponse *response; GPtrArray *headers, *messages; const char *summary_specifier; - char *p, *uid; + char *p; int i, seq, count; CamelMimeMessage *msg; CamelMessageInfo *mi; - guint32 flags, size; + GData *fetch_data; summary_specifier = imap_protocol_get_summary_specifier (store); /* We already have the command lock */ @@ -1055,58 +1016,43 @@ imap_update_summary (CamelFolder *folder, int first, int last, if (*p++ != '*' || *p++ != ' ') continue; seq = strtoul (p, &p, 10); - if (!seq || seq < count) + if (!seq || seq < first || seq > last) continue; if (g_strncasecmp (p, " FETCH (", 8) != 0) continue; - p += 8; + p += 7; mi = messages->pdata[seq - first]; - flags = size = 0; - uid = NULL; - while (p && *p != ')') { - if (*p == ' ') - p++; - if (!g_strncasecmp (p, "FLAGS ", 6)) { - p += 6; - /* FIXME user flags */ - flags = imap_parse_flag_list (&p); - } else if (!g_strncasecmp (p, "RFC822.SIZE ", 12)) { - size = strtoul (p + 12, &p, 10); - } else if (!g_strncasecmp (p, "UID ", 4)) { - uid = p + 4; - strtoul (uid, &p, 10); - uid = g_strndup (uid, p - uid); - } else if (!g_strncasecmp (p, "BODY[HEADER", 11) || - !g_strncasecmp (p, "RFC822.HEADER", 13)) { - msg = (CamelMimeMessage *) parse_headers (&p, CAMEL_MIME_MESSAGE_TYPE); - mi = camel_folder_summary_info_new_from_message (folder->summary, msg); - camel_object_unref (CAMEL_OBJECT (msg)); - } else { - g_warning ("Waiter, I did not order this %.*s", - (int)strcspn (p, " \n"), p); - p = NULL; + fetch_data = parse_fetch_response (imap_folder, p); + + if (!mi) { + CamelStream *stream; + + if (!g_datalist_get_data (&fetch_data, "BODY_PART_DATA")) { + g_datalist_clear (&fetch_data); + p = headers->pdata[i]; + g_ptr_array_remove_index (headers, i--); + g_ptr_array_add (headers, p); + continue; } - } - /* Ideally we got everything on one line, but if we - * we didn't, and we didn't get the body yet, then we - * have to postpone this line for later. - */ - if (mi == NULL) { - p = headers->pdata[i]; - g_ptr_array_remove_index (headers, i); - g_ptr_array_add (headers, p); - continue; + msg = camel_mime_message_new (); + stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM"); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream); + mi = camel_folder_summary_info_new_from_message (folder->summary, msg); + camel_object_unref (CAMEL_OBJECT (msg)); + + messages->pdata[seq - first] = mi; } - messages->pdata[seq - first] = mi; - if (uid) - camel_message_info_set_uid (mi, uid); - if (flags) - mi->flags = flags; - if (size) - mi->size = size; + if (g_datalist_get_data (&fetch_data, "UID")) + camel_message_info_set_uid (mi, g_strdup (g_datalist_get_data (&fetch_data, "UID"))); + if (g_datalist_get_data (&fetch_data, "FLAGS")) + mi->flags = GPOINTER_TO_INT (g_datalist_get_data (&fetch_data, "FLAGS")); + if (g_datalist_get_data (&fetch_data, "RFC822.SIZE")) + mi->size = GPOINTER_TO_INT (g_datalist_get_data (&fetch_data, "RFC822.SIZE")); + + g_datalist_clear (&fetch_data); } camel_imap_response_free (response); @@ -1123,6 +1069,7 @@ void camel_imap_folder_changed (CamelFolder *folder, int exists, GArray *expunged, CamelException *ex) { + CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); CamelFolderChangeInfo *changes; CamelMessageInfo *info; int len; @@ -1135,6 +1082,8 @@ camel_imap_folder_changed (CamelFolder *folder, int exists, id = g_array_index (expunged, int, i); info = camel_folder_summary_index (folder->summary, id - 1); camel_folder_change_info_remove_uid (changes, camel_message_info_uid (info)); + /* It's safe to not lock around this. */ + camel_imap_message_cache_remove (imap_folder->cache, camel_message_info_uid (info)); camel_folder_summary_remove (folder->summary, info); camel_folder_summary_info_free(folder->summary, info); } @@ -1150,3 +1099,168 @@ camel_imap_folder_changed (CamelFolder *folder, int exists, } camel_folder_change_info_free (changes); } + + +CamelStream * +camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid, + const char *section_text, gboolean cache_only, + CamelException *ex) +{ + CamelFolder *folder = CAMEL_FOLDER (imap_folder); + CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); + CamelImapResponse *response; + CamelStream *stream; + GData *fetch_data; + char *found_uid; + int i; + + CAMEL_IMAP_FOLDER_LOCK (imap_folder, cache_lock); + stream = camel_imap_message_cache_get (imap_folder->cache, uid, section_text); + if (stream || cache_only) { + CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); + return stream; + } + + CAMEL_IMAP_STORE_LOCK (store, command_lock); + if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) { + response = camel_imap_command (store, folder, ex, + "UID FETCH %s RFC822.PEEK", + uid); + } else { + response = camel_imap_command (store, folder, ex, + "UID FETCH %s BODY.PEEK[%s]", + uid, section_text); + } + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + if (!response) { + CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); + return NULL; + } + + for (i = 0; i < response->untagged->len; i++) { + fetch_data = parse_fetch_response (imap_folder, response->untagged->pdata[i]); + found_uid = g_datalist_get_data (&fetch_data, "UID"); + stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM"); + if (found_uid && stream && !strcmp (uid, found_uid)) + break; + + g_datalist_clear (&fetch_data); + stream = NULL; + } + camel_imap_response_free (response); + CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); + if (!stream) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not find message body in FETCH " + "response.")); + } else + camel_object_ref (CAMEL_OBJECT (stream)); + + g_datalist_clear (&fetch_data); + + return stream; +} + +GData * +parse_fetch_response (CamelImapFolder *imap_folder, char *response) +{ + GData *data = NULL; + char *start, *part_spec = NULL, *body = NULL, *uid = NULL; + int body_len = 0; + + if (*response != '(') { + long seq; + + if (*response != '*' || *(response + 1) != ' ') + return NULL; + seq = strtol (response + 2, &response, 10); + if (seq == 0) + return NULL; + if (g_strncasecmp (response, " FETCH (", 8) != 0) + return NULL; + response += 7; + } + + do { + /* Skip the initial '(' or the ' ' between elements */ + response++; + + if (!g_strncasecmp (response, "FLAGS ", 6)) { + guint32 flags; + + response += 6; + /* FIXME user flags */ + flags = imap_parse_flag_list (&response); + + g_datalist_set_data (&data, "FLAGS", GUINT_TO_POINTER (flags)); + } else if (!g_strncasecmp (response, "RFC822.SIZE ", 12)) { + unsigned long size; + + response += 12; + size = strtoul (response, &response, 10); + g_datalist_set_data (&data, "RFC822.SIZE", GUINT_TO_POINTER (size)); + } else if (!g_strncasecmp (response, "BODY[", 5) || + !g_strncasecmp (response, "RFC822 ", 7)) { + char *p; + + if (*response == 'B') { + response += 5; + p = strchr (response, ']'); + if (!p || *(p + 1) != ' ') + break; + part_spec = g_strndup (response, p - response); + response = p + 2; + } else { + part_spec = g_strdup (""); + response += 7; + } + + body = imap_parse_nstring (&response, &body_len); + if (!body) { + g_free (part_spec); + break; + } + + g_datalist_set_data_full (&data, "BODY_PART_SPEC", part_spec, g_free); + g_datalist_set_data_full (&data, "BODY_PART_DATA", body, g_free); + g_datalist_set_data (&data, "BODY_PART_LEN", GINT_TO_POINTER (body_len)); + } else if (!g_strncasecmp (response, "BODY ", 5) || + !g_strncasecmp (response, "BODYSTRUCTURE ", 14)) { + response = strchr (response, ' ') + 1; + start = response; + imap_skip_list (&response); + g_datalist_set_data_full (&data, "BODY", g_strndup (start, response - start), g_free); + } else if (!g_strncasecmp (response, "UID ", 4)) { + int len; + + len = strcspn (response + 4, " )"); + uid = g_strndup (response + 4, len); + g_datalist_set_data_full (&data, "UID", uid, g_free); + response += 4 + len; + } else { + g_warning ("Unexpected FETCH response from server: " + "(%s", response); + break; + } + } while (response && *response != ')'); + + if (!response || *response != ')') { + g_datalist_clear (&data); + return NULL; + } + + if (uid && body) { + CamelStream *stream; + + CAMEL_IMAP_FOLDER_LOCK (imap_folder, cache_lock); + stream = camel_imap_message_cache_insert (imap_folder->cache, + uid, part_spec, + body, body_len); + CAMEL_IMAP_FOLDER_UNLOCK (imap_folder, cache_lock); + g_datalist_set_data_full (&data, "BODY_PART_STREAM", stream, + (GDestroyNotify)camel_object_unref); + } + + return data; +} + diff --git a/camel/providers/imap/camel-imap-folder.h b/camel/providers/imap/camel-imap-folder.h index a943189f5a..6d969bf752 100644 --- a/camel/providers/imap/camel-imap-folder.h +++ b/camel/providers/imap/camel-imap-folder.h @@ -48,6 +48,7 @@ struct _CamelImapFolder { struct _CamelImapFolderPrivate *priv; CamelFolderSearch *search; + CamelImapMessageCache *cache; }; @@ -63,7 +64,7 @@ typedef struct { CamelFolder *camel_imap_folder_new (CamelStore *parent, const char *folder_name, const char *short_name, - const char *summary_file, + const char *folder_dir, CamelException *ex); void camel_imap_folder_selected (CamelFolder *folder, @@ -73,6 +74,12 @@ void camel_imap_folder_selected (CamelFolder *folder, void camel_imap_folder_changed (CamelFolder *folder, int exists, GArray *expunged, CamelException *ex); +CamelStream *camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, + const char *uid, + const char *section_text, + gboolean cache_only, + CamelException *ex); + /* Standard Camel function */ CamelType camel_imap_folder_get_type (void); 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); +} + diff --git a/camel/providers/imap/camel-imap-message-cache.h b/camel/providers/imap/camel-imap-message-cache.h new file mode 100644 index 0000000000..1ac48086ae --- /dev/null +++ b/camel/providers/imap/camel-imap-message-cache.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-imap-message-cache.h: 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 + */ + + +#ifndef CAMEL_IMAP_MESSAGE_CACHE_H +#define CAMEL_IMAP_MESSAGE_CACHE_H 1 + + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include "camel-imap-types.h" +#include "camel-folder.h" +#include <camel/camel-folder-search.h> + +#define CAMEL_IMAP_MESSAGE_CACHE_TYPE (camel_imap_message_cache_get_type ()) +#define CAMEL_IMAP_MESSAGE_CACHE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAP_MESSAGE_CACHE_TYPE, CamelImapFolder)) +#define CAMEL_IMAP_MESSAGE_CACHE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAP_MESSAGE_CACHE_TYPE, CamelImapFolderClass)) +#define CAMEL_IS_IMAP_MESSAGE_CACHE(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAP_MESSAGE_CACHE_TYPE)) + +struct _CamelImapMessageCache { + CamelObject parent_object; + + char *path; + GHashTable *parts, *cached; +}; + + +typedef struct { + CamelFolderClass parent_class; + + /* Virtual methods */ + +} CamelImapMessageCacheClass; + + +/* public methods */ +CamelImapMessageCache *camel_imap_message_cache_new (const char *path, + CamelFolderSummary *summ, + CamelException *ex); + +CamelStream *camel_imap_message_cache_insert (CamelImapMessageCache *cache, + const char *uid, + const char *part_spec, + const char *data, + int len); +CamelStream *camel_imap_message_cache_get (CamelImapMessageCache *cache, + const char *uid, + const char *part_spec); +void camel_imap_message_cache_remove (CamelImapMessageCache *cache, + const char *uid); + + +/* Standard Camel function */ +CamelType camel_imap_message_cache_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_IMAP_MESSAGE_CACHE_H */ diff --git a/camel/providers/imap/camel-imap-private.h b/camel/providers/imap/camel-imap-private.h index c7af3ee5e6..811efc5576 100644 --- a/camel/providers/imap/camel-imap-private.h +++ b/camel/providers/imap/camel-imap-private.h @@ -54,13 +54,14 @@ struct _CamelImapStorePrivate { struct _CamelImapFolderPrivate { #ifdef ENABLE_THREADS - GMutex *search_lock; /* for locking the search object */ + EMutex *search_lock; /* for locking the search object */ + EMutex *cache_lock; /* for locking the cache object */ #endif }; #ifdef ENABLE_THREADS -#define CAMEL_IMAP_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelImapFolder *)f)->priv->l)) -#define CAMEL_IMAP_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelImapFolder *)f)->priv->l)) +#define CAMEL_IMAP_FOLDER_LOCK(f, l) (e_mutex_lock(((CamelImapFolder *)f)->priv->l)) +#define CAMEL_IMAP_FOLDER_UNLOCK(f, l) (e_mutex_unlock(((CamelImapFolder *)f)->priv->l)) #else #define CAMEL_IMAP_FOLDER_LOCK(f, l) #define CAMEL_IMAP_FOLDER_UNLOCK(f, l) diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 83f9548bde..44ebea5ebe 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -32,6 +32,7 @@ #include <errno.h> #include <gal/util/e-util.h> +#include "e-util/e-path.h" #include "camel-imap-store.h" #include "camel-imap-folder.h" @@ -729,7 +730,7 @@ get_folder (CamelStore *store, const char *folder_name, guint32 flags, { CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelFolder *new_folder = NULL; - char *short_name, *summary_file, *p; + char *short_name, *folder_dir; gboolean selectable; if (!camel_remote_store_connected (CAMEL_REMOTE_STORE (store), ex)) @@ -757,22 +758,18 @@ get_folder (CamelStore *store, const char *folder_name, guint32 flags, return NULL; } - summary_file = g_strdup_printf ("%s/%s/#summary", - imap_store->storage_path, - folder_name); - p = strrchr (summary_file, '/'); - *p = '\0'; - if (e_mkdir_hier (summary_file, S_IRWXU) == 0) { - *p = '/'; + folder_dir = e_path_to_physical (imap_store->storage_path, + folder_name); + if (e_mkdir_hier (folder_dir, S_IRWXU) == 0) { new_folder = camel_imap_folder_new (store, folder_name, - short_name, summary_file, + short_name, folder_dir, ex); } else { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not create directory %s: %s"), - summary_file, g_strerror (errno)); + folder_dir, g_strerror (errno)); } - g_free (summary_file); + g_free (folder_dir); g_free (short_name); if (camel_exception_is_set (ex)) diff --git a/camel/providers/imap/camel-imap-types.h b/camel/providers/imap/camel-imap-types.h index af5d8b8c77..e5d2d18dc1 100644 --- a/camel/providers/imap/camel-imap-types.h +++ b/camel/providers/imap/camel-imap-types.h @@ -30,10 +30,11 @@ extern "C" { #include "camel-types.h" -typedef struct _CamelImapResponse CamelImapResponse; -typedef struct _CamelImapFolder CamelImapFolder; -typedef struct _CamelImapSearch CamelImapSearch; -typedef struct _CamelImapStore CamelImapStore; -typedef struct _CamelImapSummary CamelImapSummary; +typedef struct _CamelImapFolder CamelImapFolder; +typedef struct _CamelImapMessageCache CamelImapMessageCache; +typedef struct _CamelImapResponse CamelImapResponse; +typedef struct _CamelImapSearch CamelImapSearch; +typedef struct _CamelImapStore CamelImapStore; +typedef struct _CamelImapSummary CamelImapSummary; #endif /* CAMEL_IMAP_TYPES_H */ diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c index 8116f12386..1ec8e79eaf 100644 --- a/camel/providers/imap/camel-imap-utils.c +++ b/camel/providers/imap/camel-imap-utils.c @@ -337,13 +337,13 @@ skip_asn (char **str_p) } } -static void -skip_list (char **str_p) +void +imap_skip_list (char **str_p) { skip_char (str_p, '('); while (*str_p && **str_p != ')') { if (**str_p == '(') - skip_list (str_p); + imap_skip_list (str_p); else skip_asn (str_p); if (*str_p && **str_p == ' ') @@ -495,7 +495,7 @@ imap_parse_body (char **body_p, CamelFolder *folder, child = NULL; if (header_content_type_is (type, "message", "rfc822")) { skip_char (&body, ' '); - skip_list (&body); /* envelope */ + imap_skip_list (&body); /* envelope */ skip_char (&body, ' '); child = camel_folder_summary_content_info_new (folder->summary); imap_parse_body (&body, folder, child); diff --git a/camel/providers/imap/camel-imap-utils.h b/camel/providers/imap/camel-imap-utils.h index 985f177b3a..77bc51f9bc 100644 --- a/camel/providers/imap/camel-imap-utils.h +++ b/camel/providers/imap/camel-imap-utils.h @@ -55,6 +55,8 @@ void imap_parse_body (char **body_p, CamelFolder *folder, char *imap_quote_string (const char *str); +void imap_skip_list (char **str_p); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/providers/imap/camel-imap-wrapper.c b/camel/providers/imap/camel-imap-wrapper.c index e4d06a009b..a0e8b963fc 100644 --- a/camel/providers/imap/camel-imap-wrapper.c +++ b/camel/providers/imap/camel-imap-wrapper.c @@ -24,14 +24,10 @@ #include <config.h> +#include "camel-imap-folder.h" #include "camel-imap-wrapper.h" -#include "camel-imap-command.h" -#include "camel-imap-store.h" -#include "camel-imap-utils.h" #include "camel-imap-private.h" #include "camel-exception.h" -#include "camel-folder.h" -#include "camel-stream-mem.h" #include "camel-stream-filter.h" #include "camel-mime-filter-basic.h" #include "camel-mime-filter-crlf.h" @@ -109,54 +105,15 @@ camel_imap_wrapper_get_type (void) } -static int -write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream) +static void +imap_wrapper_hydrate (CamelImapWrapper *imap_wrapper, CamelStream *stream) { - CamelImapWrapper *imap_wrapper = CAMEL_IMAP_WRAPPER (data_wrapper); - CamelImapStore *store; - CamelImapResponse *response; - CamelStream *memstream; + CamelDataWrapper *data_wrapper = CAMEL_DATA_WRAPPER (imap_wrapper); CamelStreamFilter *filterstream; CamelMimeFilter *filter; CamelContentType *ct; - char *result, *p, *body; - int len; - CAMEL_IMAP_WRAPPER_LOCK (imap_wrapper, lock); - if (!data_wrapper->offline) { - CAMEL_IMAP_WRAPPER_UNLOCK (imap_wrapper, lock); - return parent_class->write_to_stream (data_wrapper, stream); - } - - store = CAMEL_IMAP_STORE (imap_wrapper->folder->parent_store); - CAMEL_IMAP_STORE_LOCK (store, command_lock); - response = camel_imap_command (store, imap_wrapper->folder, NULL, - "UID FETCH %s BODY.PEEK[%s]", - imap_wrapper->uid, - imap_wrapper->part_spec); - CAMEL_IMAP_STORE_UNLOCK (store, command_lock); - if (!response) - goto lose; - - result = camel_imap_response_extract (response, "FETCH", NULL); - if (!result) - goto lose; - - p = strchr (result, ']'); - if (!p) { - g_free (result); - goto lose; - } - p += 2; - - body = imap_parse_nstring (&p, &len); - g_free (result); - if (!body) - goto lose; - - memstream = camel_stream_mem_new_with_buffer (body, len); - g_free (body); - filterstream = camel_stream_filter_new_with_stream (memstream); + filterstream = camel_stream_filter_new_with_stream (stream); if (camel_mime_part_get_encoding (imap_wrapper->part) == CAMEL_MIME_PART_ENCODING_BASE64) { @@ -198,37 +155,64 @@ write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream) imap_wrapper->uid = NULL; g_free (imap_wrapper->part_spec); imap_wrapper->part = NULL; +} - CAMEL_IMAP_WRAPPER_UNLOCK (imap_wrapper, lock); - return parent_class->write_to_stream (data_wrapper, stream); +static int +write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream) +{ + CamelImapWrapper *imap_wrapper = CAMEL_IMAP_WRAPPER (data_wrapper); + + CAMEL_IMAP_WRAPPER_LOCK (imap_wrapper, lock); + if (data_wrapper->offline) { + CamelStream *datastream; + + datastream = camel_imap_folder_fetch_data ( + imap_wrapper->folder, imap_wrapper->uid, + imap_wrapper->part_spec, FALSE, NULL); + if (!datastream) { + CAMEL_IMAP_WRAPPER_UNLOCK (imap_wrapper, lock); + errno = ENETUNREACH; + return -1; + } - lose: + imap_wrapper_hydrate (imap_wrapper, datastream); + camel_object_unref (CAMEL_OBJECT (datastream)); + } CAMEL_IMAP_WRAPPER_UNLOCK (imap_wrapper, lock); - errno = ENETUNREACH; - return -1; + + return parent_class->write_to_stream (data_wrapper, stream); } CamelDataWrapper * -camel_imap_wrapper_new (CamelFolder *folder, CamelContentType *type, +camel_imap_wrapper_new (CamelImapFolder *imap_folder, CamelContentType *type, const char *uid, const char *part_spec, CamelMimePart *part) { CamelImapWrapper *imap_wrapper; + CamelStream *stream; imap_wrapper = (CamelImapWrapper *)camel_object_new(camel_imap_wrapper_get_type()); camel_data_wrapper_set_mime_type_field (CAMEL_DATA_WRAPPER (imap_wrapper), type); ((CamelDataWrapper *)imap_wrapper)->offline = TRUE; - imap_wrapper->folder = folder; - camel_object_ref (CAMEL_OBJECT (folder)); + imap_wrapper->folder = imap_folder; + camel_object_ref (CAMEL_OBJECT (imap_folder)); imap_wrapper->uid = g_strdup (uid); imap_wrapper->part_spec = g_strdup (part_spec); /* Don't ref this, it's our parent. */ imap_wrapper->part = part; + /* Try the cache. */ + stream = camel_imap_folder_fetch_data (imap_folder, uid, part_spec, + TRUE, NULL); + if (stream) { + imap_wrapper_hydrate (imap_wrapper, stream); + camel_object_unref (CAMEL_OBJECT (stream)); + } + return (CamelDataWrapper *)imap_wrapper; } diff --git a/camel/providers/imap/camel-imap-wrapper.h b/camel/providers/imap/camel-imap-wrapper.h index 8eb9d0969a..38bfd62fe2 100644 --- a/camel/providers/imap/camel-imap-wrapper.h +++ b/camel/providers/imap/camel-imap-wrapper.h @@ -31,6 +31,7 @@ extern "C" { #endif /* __cplusplus }*/ #include <camel/camel-data-wrapper.h> +#include "camel-imap-types.h" #define CAMEL_IMAP_WRAPPER_TYPE (camel_imap_wrapper_get_type ()) #define CAMEL_IMAP_WRAPPER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAP_WRAPPER_TYPE, CamelImapWrapper)) @@ -43,7 +44,7 @@ typedef struct struct _CamelImapWrapperPrivate *priv; - CamelFolder *folder; + CamelImapFolder *folder; char *uid, *part_spec; CamelMimePart *part; } CamelImapWrapper; @@ -57,7 +58,7 @@ typedef struct { CamelType camel_imap_wrapper_get_type (void); /* Constructor */ -CamelDataWrapper *camel_imap_wrapper_new (CamelFolder *folder, +CamelDataWrapper *camel_imap_wrapper_new (CamelImapFolder *imap_folder, CamelContentType *type, const char *uid, const char *part_spec, |