diff options
-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, |