aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/imap
diff options
context:
space:
mode:
authorDan Winship <danw@src.gnome.org>2001-01-17 08:27:19 +0800
committerDan Winship <danw@src.gnome.org>2001-01-17 08:27:19 +0800
commit8ad855fef6632e32723242fda554fce04f025036 (patch)
treefea4a94685bb6504bb679111f0e9e84e0f9ad835 /camel/providers/imap
parent35edf4f02ace2559daea85c11c8f36efc7fe0b80 (diff)
downloadgsoc2013-evolution-8ad855fef6632e32723242fda554fce04f025036.tar.gz
gsoc2013-evolution-8ad855fef6632e32723242fda554fce04f025036.tar.zst
gsoc2013-evolution-8ad855fef6632e32723242fda554fce04f025036.zip
Delayed loading of IMAP message parts.
* camel-types.h: typedef CamelMessageInfo and CamelMessageContentInfo here * camel-folder-summary.h: Add a "size" field to CamelMessageContentInfo. * camel-folder-summary.c (camel_folder_summary_content_info_new, camel_folder_summary_content_info_free): Renamed and made non-static for providers that construct their own content info. (content_info_load, content_info_save): load/save size * camel-data-wrapper.c (camel_data_wrapper_is_offline): New function to return if a data wrapper's contents are "offline". (So that, for example, we don't make thumbnails of images that haven't been loaded off the IMAP server yet.) Defaults to FALSE. * providers/imap/camel-imap-folder.c (camel_imap_folder_selected): Fix a bug in re-selecting a folder when messages have been expunged from it by another client in the meantime. (imap_get_message): Rewrite. If the message is larger than a certain size, just create a skeleton message containing CamelImapWrappers that will read parts as needed. This way, large attachments only need to be downloaded if the user looks at them, and multipart/alternative alternatives that aren't used will never be downloaded at all. (imap_update_summary): Rewrite this a bunch too to make the parsing more robust. * providers/imap/camel-imap-summary.c (CAMEL_IMAP_SUMMARY_VERSION): bump. (camel_imap_summary_new): Set build_content to TRUE. (content_info_load, content_info_save): Only save/load the content for messages that have it. (The content info gets created as a side effect of imap_get_message.) * providers/imap/camel-imap-utils.c (imap_parse_body): New routine (and helpers) to parse an IMAP 'body' FETCH response and fill in a CamelMessageContentInfo from it. * providers/imap/Makefile.am (libcamelimap_la_SOURCES, libcamelimap_la_HEADERS): add camel-imap-wrapper. svn path=/trunk/; revision=7557
Diffstat (limited to 'camel/providers/imap')
-rw-r--r--camel/providers/imap/Makefile.am6
-rw-r--r--camel/providers/imap/camel-imap-folder.c468
-rw-r--r--camel/providers/imap/camel-imap-summary.c34
-rw-r--r--camel/providers/imap/camel-imap-utils.c250
-rw-r--r--camel/providers/imap/camel-imap-utils.h3
-rw-r--r--camel/providers/imap/camel-imap-wrapper.c212
-rw-r--r--camel/providers/imap/camel-imap-wrapper.h68
7 files changed, 884 insertions, 157 deletions
diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am
index aa34eac652..341fa6b421 100644
--- a/camel/providers/imap/Makefile.am
+++ b/camel/providers/imap/Makefile.am
@@ -28,7 +28,8 @@ libcamelimap_la_SOURCES = \
camel-imap-search.c \
camel-imap-store.c \
camel-imap-summary.c \
- camel-imap-utils.c
+ camel-imap-utils.c \
+ camel-imap-wrapper.c
libcamelimapinclude_HEADERS = \
camel-imap-auth.h \
@@ -38,7 +39,8 @@ libcamelimapinclude_HEADERS = \
camel-imap-store.h \
camel-imap-summary.h \
camel-imap-types.h \
- camel-imap-utils.h
+ camel-imap-utils.h \
+ camel-imap-wrapper.h
libcamelimap_la_LDFLAGS = $(KRB4_LDFLAGS) -version-info 0:0:0
diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c
index 373ded8bde..1c8d793074 100644
--- a/camel/providers/imap/camel-imap-folder.c
+++ b/camel/providers/imap/camel-imap-folder.c
@@ -43,6 +43,7 @@
#include "camel-imap-store.h"
#include "camel-imap-summary.h"
#include "camel-imap-utils.h"
+#include "camel-imap-wrapper.h"
#include "string-utils.h"
#include "camel-stream.h"
#include "camel-stream-fs.h"
@@ -56,6 +57,7 @@
#include "camel-exception.h"
#include "camel-mime-utils.h"
#include "camel-imap-private.h"
+#include "camel-multipart.h"
#define d(x) x
@@ -235,7 +237,7 @@ camel_imap_folder_selected (CamelFolder *folder, CamelImapResponse *response,
/* If we've lost messages, we have to rescan everything */
if (exists < count) {
- imap_rescan (folder, count, ex);
+ imap_rescan (folder, exists, ex);
return;
}
@@ -596,87 +598,298 @@ imap_move_message_to (CamelFolder *source, const char *uid,
camel_folder_delete_message (source, uid);
}
-static CamelMimeMessage *
-imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
+static GPtrArray *
+imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex)
+{
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+ GPtrArray *matches, *summary;
+
+ /* we could get around this by creating a new search object each time,
+ but i doubt its worth it since any long operation would lock the
+ command channel too */
+ CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
+
+ if (!imap_folder->search)
+ imap_folder->search = camel_imap_search_new ();
+
+ camel_folder_search_set_folder (imap_folder->search, folder);
+ summary = camel_folder_get_summary(folder);
+ camel_folder_search_set_summary(imap_folder->search, summary);
+ matches = camel_folder_search_execute_expression (imap_folder->search, expression, ex);
+
+ CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock);
+
+ camel_folder_free_summary(folder, summary);
+
+ return matches;
+}
+
+static void
+imap_search_free (CamelFolder *folder, GPtrArray *uids)
+{
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+
+ g_return_if_fail (imap_folder->search);
+
+ CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
+
+ camel_folder_search_free_result (imap_folder->search, 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;
- CamelStream *msgstream;
- CamelMimeMessage *msg;
- char *result, *mesg, *p;
- int len;
+ CamelMedium *medium;
+ char *result, *p;
- CAMEL_IMAP_STORE_LOCK(store, command_lock);
+ CAMEL_IMAP_STORE_LOCK (store, command_lock);
response = camel_imap_command (store, folder, ex,
- "UID FETCH %s BODY.PEEK[]", uid);
- CAMEL_IMAP_STORE_UNLOCK(store, command_lock);
-
+ "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;
- p = strstr (result, "BODY[]");
- if (p) {
- p += 7;
- mesg = imap_parse_nstring (&p, &len);
- }
- if (!p) {
+ 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."));
- g_free (result);
- return NULL;
+ medium = NULL;
}
g_free (result);
- msgstream = camel_stream_mem_new_with_buffer (mesg, len);
- msg = camel_mime_message_new ();
- camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg),
- msgstream);
- camel_object_unref (CAMEL_OBJECT (msgstream));
- g_free (mesg);
+ return medium;
+}
+
+static CamelMimeMessage *get_message (CamelFolder *folder, const char *uid,
+ const char *part_specifier,
+ CamelMessageContentInfo *ci,
+ CamelException *ex);
+
+/* Fetch the contents of the MIME part indicated by @ci, which is part
+ * of message @uid in @folder.
+ */
+static CamelDataWrapper *
+get_content (CamelFolder *folder, const char *uid, const char *part_spec,
+ CamelMimePart *part, CamelMessageContentInfo *ci,
+ CamelException *ex)
+{
+ 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 ();
+ camel_data_wrapper_set_mime_type_field (
+ CAMEL_DATA_WRAPPER (body_mp), ci->type);
+ camel_multipart_set_boundary (body_mp, NULL);
+
+ speclen = strlen (part_spec);
+ child_spec = g_malloc (speclen + 15);
+ memcpy (child_spec, part_spec, speclen);
+ if (speclen > 0)
+ child_spec[speclen++] = '.';
+
+ ci = ci->childs;
+ 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) {
+ 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_object_unref (CAMEL_OBJECT (content));
+ camel_multipart_add_part (body_mp, part);
+ camel_object_unref (CAMEL_OBJECT (part));
+
+ ci = ci->next;
+ }
+ g_free (child_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);
+ } 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);
+ g_free (child_spec);
+ return content;
+ }
+}
+
+static CamelMimeMessage *
+get_message (CamelFolder *folder, const char *uid, const char *part_spec,
+ CamelMessageContentInfo *ci, CamelException *ex)
+{
+ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
+ CamelDataWrapper *content;
+ CamelMimeMessage *msg;
+ 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);
+ g_free (section_text);
+ if (!msg)
+ return NULL;
+
+ content = get_content (folder, uid, part_spec, CAMEL_MIME_PART (msg), ci, ex);
+ if (!content) {
+ camel_object_unref (CAMEL_OBJECT (msg));
+ return NULL;
+ }
+
+ camel_medium_set_content_object (CAMEL_MEDIUM (msg), content);
+ camel_object_unref (CAMEL_OBJECT (content));
return msg;
}
-/**
- * imap_protocol_get_summary_specifier
- *
- * Make a data item specifier for the header lines we need,
- * appropriate to the server level.
- **/
-static char *
-imap_protocol_get_summary_specifier (CamelImapStore *store)
+/* FIXME: I pulled this number out of my butt. */
+#define IMAP_SMALL_BODY_SIZE 5120
+
+static CamelMimeMessage *
+imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
{
- char *sect_begin, *sect_end;
- char *headers_wanted = "SUBJECT FROM TO CC DATE MESSAGE-ID REFERENCES IN-REPLY-TO";
+ CamelMessageInfo *mi;
+ CamelMimeMessage *msg;
- if (store->server_level >= IMAP_LEVEL_IMAP4REV1) {
- sect_begin = "BODY.PEEK[HEADER.FIELDS";
- sect_end = "]";
- } else {
- sect_begin = "RFC822.HEADER.LINES";
- sect_end = "";
+ mi = camel_folder_summary_uid (folder->summary, uid);
+ g_return_val_if_fail (mi != NULL, NULL);
+
+ /* 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);
}
- return g_strdup_printf ("UID FLAGS RFC822.SIZE %s (%s)%s", sect_begin,
- headers_wanted, sect_end);
+ /* For larger messages, fetch the structure and build a message
+ * with offline parts. (We check mi->content->type rather than
+ * mi->content because camel_folder_summary_info_new always creates
+ * an empty content struct.)
+ */
+ if (!mi->content->type) {
+ CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
+ CamelImapResponse *response;
+ char *result, *p;
+
+ CAMEL_IMAP_STORE_LOCK (store, command_lock);
+ response = camel_imap_command (store, folder, ex,
+ "UID FETCH %s BODY", uid);
+ CAMEL_IMAP_STORE_UNLOCK (store, command_lock);
+ if (!response) {
+ 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);
+ }
+ g_free (result);
+ if (!mi->content->type) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("Could not find message body in FETCH response."));
+ camel_folder_summary_info_free (folder->summary, mi);
+ return NULL;
+ }
+ }
+
+ msg = get_message (folder, uid, "", mi->content, ex);
+ camel_folder_summary_info_free (folder->summary, mi);
+
+ return msg;
+}
+
+static const char *
+imap_protocol_get_summary_specifier (CamelImapStore *store)
+{
+ if (store->server_level >= IMAP_LEVEL_IMAP4REV1)
+ return "UID FLAGS RFC822.SIZE BODY.PEEK[HEADER]";
+ else
+ return "UID FLAGS RFC822.SIZE RFC822.HEADER";
}
-/* Called with the store's command_lock locked */
static void
imap_update_summary (CamelFolder *folder, int first, int last,
CamelFolderChangeInfo *changes, CamelException *ex)
{
CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
- /*CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);*/
CamelImapResponse *response;
- GPtrArray *headers = NULL;
- char *q, *summary_specifier;
- struct _header_raw *h = NULL;
- int i;
+ GPtrArray *headers, *messages;
+ const char *summary_specifier;
+ char *p, *uid;
+ int i, seq, count;
+ CamelMimeMessage *msg;
+ CamelMessageInfo *mi;
+ guint32 flags, size;
summary_specifier = imap_protocol_get_summary_specifier (store);
/* We already have the command lock */
@@ -689,118 +902,79 @@ imap_update_summary (CamelFolder *folder, int first, int last,
"FETCH %d:%d (%s)", first,
last, summary_specifier);
}
- g_free (summary_specifier);
if (!response)
return;
+ count = camel_folder_summary_count (folder->summary);
+ messages = g_ptr_array_new ();
+ g_ptr_array_set_size (messages, last - first + 1);
headers = response->untagged;
for (i = 0; i < headers->len; i++) {
- CamelMessageInfo *info;
- CamelImapMessageInfo *iinfo;
- char *uid, *flags, *header, *size;
-
- /* Grab the UID... */
- if (!(uid = strstr (headers->pdata[i], "UID "))) {
- d(fprintf (stderr, "Cannot get a uid for %d\n\n%s\n\n", i+1, (char *) headers->pdata[i]));
- break;
+ p = headers->pdata[i];
+ if (*p++ != '*' || *p++ != ' ')
+ continue;
+ seq = strtoul (p, &p, 10);
+ if (!seq || seq < count)
+ continue;
+ if (g_strncasecmp (p, " FETCH (", 8) != 0)
+ continue;
+ p += 8;
+
+ 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;
+ }
}
- for (uid += 4; *uid && (*uid < '0' || *uid > '9'); uid++)
- ;
- for (q = uid; *q && *q >= '0' && *q <= '9'; q++)
- ;
-
- /* construct the header list */
- /* fast-forward to beginning of header info... */
- header = strchr (headers->pdata[i], '\n') + 1;
- h = NULL;
- do {
- char *line;
- int len;
-
- len = strcspn (header, "\n");
- while (header[len + 1] == ' ' ||
- header[len + 1] == '\t')
- len += 1 + strcspn (header + len + 1, "\n");
- line = g_strndup (header, len);
- header_raw_append_parse (&h, line, -1);
- g_free (line);
-
- header += len;
- } while (*header++ == '\n' && *header != '\n');
-
- /* We can't just call camel_folder_summary_add_from_parser
- * because it will assign the wrong UID, and thus get the
- * uid hash table wrong and all that. FIXME some day.
- * Well you can actually now, because you can override next_uid_string(), but
- * it hasn't been done yet.
+ /* 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.
*/
- info = camel_folder_summary_info_new_from_header(folder->summary, h);
- iinfo = (CamelImapMessageInfo *)info;
- header_raw_clear (&h);
- uid = g_strndup (uid, q - uid);
- camel_folder_change_info_add_uid (changes, uid);
- camel_message_info_set_uid (info, uid);
-
- /* now lets grab the FLAGS */
- if (!(flags = strstr (headers->pdata[i], "FLAGS "))) {
- d(fprintf (stderr, "We didn't seem to get any flags for %d...\n", i));
- } else {
- flags += 6;
- info->flags = imap_parse_flag_list (&flags);
- iinfo->server_flags = info->flags;
+ if (mi == NULL) {
+ p = headers->pdata[i];
+ g_ptr_array_remove_index (headers, i);
+ g_ptr_array_add (headers, p);
+ continue;
}
- /* And size */
- if (!(size = strstr (headers->pdata[i], "RFC822.SIZE "))) {
- d(fprintf (stderr, "We didn't seem to get any size for %d...\n", i));
- } else
- info->size = strtoul (size + 12, NULL, 10);
-
- camel_folder_summary_add (folder->summary, info);
+ messages->pdata[seq - first] = mi;
+ if (uid)
+ camel_message_info_set_uid (mi, uid);
+ if (flags)
+ mi->flags = flags;
+ if (size)
+ mi->size = size;
}
camel_imap_response_free (response);
-}
-
-static GPtrArray *
-imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex)
-{
- CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
- GPtrArray *matches, *summary;
-
- /* we could get around this by creating a new search object each time,
- but i doubt its worth it since any long operation would lock the
- command channel too */
- CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
-
- if (!imap_folder->search)
- imap_folder->search = camel_imap_search_new ();
-
- camel_folder_search_set_folder (imap_folder->search, folder);
- summary = camel_folder_get_summary(folder);
- camel_folder_search_set_summary(imap_folder->search, summary);
- matches = camel_folder_search_execute_expression (imap_folder->search, expression, ex);
- CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock);
-
- camel_folder_free_summary(folder, summary);
-
- return matches;
-}
-
-static void
-imap_search_free (CamelFolder *folder, GPtrArray *uids)
-{
- CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
-
- g_return_if_fail (imap_folder->search);
-
- CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
-
- camel_folder_search_free_result (imap_folder->search, uids);
-
- CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock);
+ for (i = 0; i < messages->len; i++) {
+ mi = messages->pdata[i];
+ camel_folder_summary_add (folder->summary, mi);
+ }
+ g_ptr_array_free (messages, TRUE);
}
/* Called with the store's command_lock locked */
diff --git a/camel/providers/imap/camel-imap-summary.c b/camel/providers/imap/camel-imap-summary.c
index 5d657b6d68..dd77673cad 100644
--- a/camel/providers/imap/camel-imap-summary.c
+++ b/camel/providers/imap/camel-imap-summary.c
@@ -32,14 +32,17 @@
#include <string.h>
#include <stdlib.h>
-#define CAMEL_IMAP_SUMMARY_VERSION (0x2000)
+#define CAMEL_IMAP_SUMMARY_VERSION (0x300)
static int summary_header_load (CamelFolderSummary *, FILE *);
static int summary_header_save (CamelFolderSummary *, FILE *);
static CamelMessageInfo *message_info_load (CamelFolderSummary *s, FILE *in);
-static int message_info_save (CamelFolderSummary *s, FILE *out,
- CamelMessageInfo *info);
+static int message_info_save (CamelFolderSummary *s, FILE *out,
+ CamelMessageInfo *info);
+static CamelMessageContentInfo *content_info_load (CamelFolderSummary *s, FILE *in);
+static int content_info_save (CamelFolderSummary *s, FILE *out,
+ CamelMessageContentInfo *info);
static void camel_imap_summary_class_init (CamelImapSummaryClass *klass);
static void camel_imap_summary_init (CamelImapSummary *obj);
@@ -76,6 +79,8 @@ camel_imap_summary_class_init (CamelImapSummaryClass *klass)
cfs_class->summary_header_save = summary_header_save;
cfs_class->message_info_load = message_info_load;
cfs_class->message_info_save = message_info_save;
+ cfs_class->content_info_load = content_info_load;
+ cfs_class->content_info_save = content_info_save;
}
static void
@@ -109,7 +114,7 @@ camel_imap_summary_new (const char *filename, guint32 validity)
camel_object_new (camel_imap_summary_get_type ()));
CamelImapSummary *imap_summary = (CamelImapSummary *)summary;
- camel_folder_summary_set_build_content (summary, FALSE);
+ camel_folder_summary_set_build_content (summary, TRUE);
camel_folder_summary_set_filename (summary, filename);
if (camel_folder_summary_load (summary) == -1) {
@@ -186,3 +191,24 @@ message_info_save (CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
return camel_folder_summary_encode_uint32 (out, iinfo->server_flags);
}
+
+
+static CamelMessageContentInfo *
+content_info_load (CamelFolderSummary *s, FILE *in)
+{
+ if (fgetc (in))
+ return camel_imap_summary_parent->content_info_load (s, in);
+ else
+ return camel_folder_summary_content_info_new (s);
+}
+
+static int
+content_info_save (CamelFolderSummary *s, FILE *out,
+ CamelMessageContentInfo *info)
+{
+ if (info->type) {
+ fputc (1, out);
+ return camel_imap_summary_parent->content_info_save (s, out, info);
+ } else
+ return fputc (0, out);
+}
diff --git a/camel/providers/imap/camel-imap-utils.c b/camel/providers/imap/camel-imap-utils.c
index 1b4aaba499..8116f12386 100644
--- a/camel/providers/imap/camel-imap-utils.c
+++ b/camel/providers/imap/camel-imap-utils.c
@@ -25,11 +25,9 @@
#include <string.h>
#include <time.h>
-#include <gtk/gtk.h>
#include "camel-imap-utils.h"
-#include "string-utils.h"
-#include <e-sexp.h>
-#include "camel/camel-folder-summary.h"
+#include "camel-imap-summary.h"
+#include "camel-folder.h"
#define d(x) x
@@ -290,6 +288,250 @@ imap_parse_string_generic (char **str_p, int *len, int type)
}
}
+static inline void
+skip_char (char **str_p, char ch)
+{
+ if (*str_p && **str_p == ch)
+ *str_p = *str_p + 1;
+ else
+ *str_p = NULL;
+}
+
+/* Skip atom, string, or number */
+static void
+skip_asn (char **str_p)
+{
+ char *str = *str_p;
+
+ if (!str)
+ return;
+ else if (*str == '"') {
+ while (*++str && *str != '"') {
+ if (*str == '\\') {
+ str++;
+ if (!*str)
+ break;
+ }
+ }
+ if (*str == '"')
+ *str_p = str + 1;
+ else
+ *str_p = NULL;
+ } else if (*str == '{') {
+ unsigned long len;
+
+ len = strtoul (str + 1, &str, 10);
+ if (*str != '}' || *(str + 1) != '\n' ||
+ strlen (str + 2) < len) {
+ *str_p = NULL;
+ return;
+ }
+ *str_p = str + 2 + len;
+ } else {
+ /* We assume the string is well-formed and don't
+ * bother making sure it's a valid atom.
+ */
+ while (*str && *str != ')' && *str != ' ')
+ str++;
+ *str_p = str;
+ }
+}
+
+static void
+skip_list (char **str_p)
+{
+ skip_char (str_p, '(');
+ while (*str_p && **str_p != ')') {
+ if (**str_p == '(')
+ skip_list (str_p);
+ else
+ skip_asn (str_p);
+ if (*str_p && **str_p == ' ')
+ skip_char (str_p, ' ');
+ }
+ skip_char (str_p, ')');
+}
+
+static void
+parse_params (char **parms_p, CamelContentType *type)
+{
+ char *parms = *parms_p, *name, *value;
+ int len;
+
+ if (!g_strncasecmp (parms, "nil", 3)) {
+ *parms_p += 3;
+ return;
+ }
+
+ if (*parms++ != '(') {
+ *parms_p = NULL;
+ return;
+ }
+
+ while (parms && *parms != ')') {
+ name = imap_parse_nstring (&parms, &len);
+ skip_char (&parms, ' ');
+ value = imap_parse_nstring (&parms, &len);
+
+ if (name && value)
+ header_content_type_set_param (type, name, value);
+ g_free (name);
+ g_free (value);
+
+ if (parms && *parms == ' ')
+ parms++;
+ }
+
+ if (!parms || *parms++ != ')') {
+ *parms_p = NULL;
+ return;
+ }
+ *parms_p = parms;
+}
+
+/**
+ * imap_parse_body:
+ * @body_p: pointer to the start of an IMAP "body"
+ * @folder: an imap folder
+ * @ci: a CamelMessageContentInfo to fill in
+ *
+ * This filles in @ci with data from *@body_p. On success *@body_p
+ * will point to the character after the body. On failure, it will be
+ * set to %NULL and @ci will be unchanged.
+ **/
+void
+imap_parse_body (char **body_p, CamelFolder *folder,
+ CamelMessageContentInfo *ci)
+{
+ char *body = *body_p;
+ CamelMessageContentInfo *child;
+ CamelContentType *type;
+ int len;
+
+ if (*body++ != '(') {
+ *body_p = NULL;
+ return;
+ }
+
+ if (*body == '(') {
+ /* multipart */
+ GPtrArray *children;
+ char *subtype;
+ int i;
+
+ /* Parse the child body parts */
+ children = g_ptr_array_new ();
+ i = 0;
+ while (body && *body == '(') {
+ child = camel_folder_summary_content_info_new (folder->summary);
+ g_ptr_array_add (children, child);
+ imap_parse_body (&body, folder, child);
+ if (!body)
+ break;
+ child->parent = ci;
+ }
+ skip_char (&body, ' ');
+
+ /* Parse the multipart subtype */
+ subtype = imap_parse_string (&body, &len);
+
+ /* If there is a parse error, abort. */
+ if (!body) {
+ for (i = 0; i < children->len; i++) {
+ child = children->pdata[i];
+ camel_folder_summary_content_info_free (folder->summary, child);
+ }
+ g_ptr_array_free (children, TRUE);
+ *body_p = NULL;
+ return;
+ }
+
+ g_strdown (subtype);
+ ci->type = header_content_type_new ("multipart", subtype);
+ g_free (subtype);
+
+ /* Chain the children. */
+ ci->childs = children->pdata[0];
+ ci->size = 0;
+ for (i = 0; i < children->len - 1; i++) {
+ child = children->pdata[i];
+ child->next = children->pdata[i + 1];
+ ci->size += child->size;
+ }
+ g_ptr_array_free (children, TRUE);
+ } else {
+ /* single part */
+ char *main_type, *subtype;
+ char *id, *description, *encoding;
+ guint32 size;
+
+ main_type = imap_parse_string (&body, &len);
+ skip_char (&body, ' ');
+ subtype = imap_parse_string (&body, &len);
+ skip_char (&body, ' ');
+ if (!body) {
+ g_free (main_type);
+ g_free (subtype);
+ *body_p = NULL;
+ return;
+ }
+ g_strdown (main_type);
+ g_strdown (subtype);
+ type = header_content_type_new (main_type, subtype);
+ g_free (main_type);
+ g_free (subtype);
+ parse_params (&body, type);
+ skip_char (&body, ' ');
+
+ id = imap_parse_nstring (&body, &len);
+ skip_char (&body, ' ');
+ description = imap_parse_nstring (&body, &len);
+ skip_char (&body, ' ');
+ encoding = imap_parse_string (&body, &len);
+ skip_char (&body, ' ');
+ if (body)
+ size = strtoul (body, &body, 10);
+
+ child = NULL;
+ if (header_content_type_is (type, "message", "rfc822")) {
+ skip_char (&body, ' ');
+ skip_list (&body); /* envelope */
+ skip_char (&body, ' ');
+ child = camel_folder_summary_content_info_new (folder->summary);
+ imap_parse_body (&body, folder, child);
+ if (!body)
+ camel_folder_summary_content_info_free (folder->summary, child);
+ skip_char (&body, ' ');
+ if (body)
+ strtoul (body, &body, 10);
+ } else if (header_content_type_is (type, "text", "*")) {
+ if (body)
+ strtoul (body, &body, 10);
+ }
+
+ if (body) {
+ ci->type = type;
+ ci->id = id;
+ ci->description = description;
+ ci->encoding = encoding;
+ ci->size = size;
+ ci->childs = child;
+ } else {
+ header_content_type_unref (type);
+ g_free (id);
+ g_free (description);
+ g_free (encoding);
+ }
+ }
+
+ if (!body || *body++ != ')') {
+ *body_p = NULL;
+ return;
+ }
+
+ *body_p = body;
+}
+
/**
* imap_quote_string:
* @str: the string to quote, which must not contain CR or LF
diff --git a/camel/providers/imap/camel-imap-utils.h b/camel/providers/imap/camel-imap-utils.h
index d6df958ac9..985f177b3a 100644
--- a/camel/providers/imap/camel-imap-utils.h
+++ b/camel/providers/imap/camel-imap-utils.h
@@ -50,6 +50,9 @@ char *imap_parse_string_generic (char **str_p, int *len, int type);
#define imap_parse_astring(str_p, len_p) \
imap_parse_string_generic (str_p, len_p, IMAP_ASTRING)
+void imap_parse_body (char **body_p, CamelFolder *folder,
+ CamelMessageContentInfo *ci);
+
char *imap_quote_string (const char *str);
#ifdef __cplusplus
diff --git a/camel/providers/imap/camel-imap-wrapper.c b/camel/providers/imap/camel-imap-wrapper.c
new file mode 100644
index 0000000000..2277b7747f
--- /dev/null
+++ b/camel/providers/imap/camel-imap-wrapper.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; -*- */
+/* camel-imap-wrapper.c: data wrapper for offline IMAP data */
+
+/*
+ * Author: Dan Winship <danw@helixcode.com>
+ *
+ * Copyright 2000 Helix Code, Inc. (http://www.helixcode.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 "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"
+#include "camel-mime-filter-charset.h"
+#include "camel-mime-part.h"
+
+#include <errno.h>
+#include <string.h>
+
+static CamelDataWrapperClass *parent_class = NULL;
+
+/* Returns the class for a CamelDataWrapper */
+#define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static int write_to_stream (CamelDataWrapper *imap_wrapper, CamelStream *stream);
+
+static void
+camel_imap_wrapper_class_init (CamelImapWrapperClass *camel_imap_wrapper_class)
+{
+ CamelDataWrapperClass *camel_data_wrapper_class =
+ CAMEL_DATA_WRAPPER_CLASS (camel_imap_wrapper_class);
+
+ parent_class = CAMEL_DATA_WRAPPER_CLASS (camel_type_get_global_classfuncs (camel_data_wrapper_get_type ()));
+
+ /* virtual method override */
+ camel_data_wrapper_class->write_to_stream = write_to_stream;
+}
+
+static void
+camel_imap_wrapper_finalize (CamelObject *object)
+{
+ CamelImapWrapper *imap_wrapper = CAMEL_IMAP_WRAPPER (object);
+
+ if (imap_wrapper->folder)
+ camel_object_unref (CAMEL_OBJECT (imap_wrapper->folder));
+ if (imap_wrapper->uid)
+ g_free (imap_wrapper->uid);
+ if (imap_wrapper->part)
+ g_free (imap_wrapper->part_spec);
+}
+
+CamelType
+camel_imap_wrapper_get_type (void)
+{
+ static CamelType camel_imap_wrapper_type = CAMEL_INVALID_TYPE;
+
+ if (camel_imap_wrapper_type == CAMEL_INVALID_TYPE) {
+ camel_imap_wrapper_type = camel_type_register (
+ CAMEL_DATA_WRAPPER_TYPE, "CamelImapWrapper",
+ sizeof (CamelImapWrapper),
+ sizeof (CamelImapWrapperClass),
+ (CamelObjectClassInitFunc) camel_imap_wrapper_class_init,
+ NULL,
+ NULL,
+ (CamelObjectFinalizeFunc) camel_imap_wrapper_finalize);
+ }
+
+ return camel_imap_wrapper_type;
+}
+
+
+static int
+write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
+{
+ CamelImapWrapper *imap_wrapper = CAMEL_IMAP_WRAPPER (data_wrapper);
+ CamelImapStore *store;
+ CamelImapResponse *response;
+ CamelStream *memstream;
+ CamelStreamFilter *filterstream;
+ CamelMimeFilter *filter;
+ CamelContentType *ct;
+ char *result, *p, *body;
+ int len;
+
+ if (!data_wrapper->offline)
+ 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);
+
+ if (camel_mime_part_get_encoding (imap_wrapper->part) ==
+ CAMEL_MIME_PART_ENCODING_BASE64) {
+ filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+ camel_stream_filter_add (filterstream, filter);
+ } else if (camel_mime_part_get_encoding (imap_wrapper->part) ==
+ CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE) {
+ filter = (CamelMimeFilter *)camel_mime_filter_basic_new_type (CAMEL_MIME_FILTER_BASIC_QP_DEC);
+ camel_stream_filter_add (filterstream, filter);
+ } else
+ filter = NULL;
+
+ ct = camel_mime_part_get_content_type (imap_wrapper->part);
+ if (header_content_type_is (ct, "text", "*")) {
+ const char *charset;
+
+ /* If we just did B64/QP, need to also do CRLF->LF */
+ if (filter) {
+ filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_DECODE,
+ CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY);
+ camel_stream_filter_add (filterstream, filter);
+ }
+
+ charset = header_content_type_param (ct, "charset");
+ if (charset && !(strcasecmp (charset, "us-ascii") == 0
+ || strcasecmp (charset, "utf-8") == 0)) {
+ filter = (CamelMimeFilter *)camel_mime_filter_charset_new_convert (charset, "UTF-8");
+ if (filter)
+ camel_stream_filter_add (filterstream, filter);
+ }
+ }
+
+ data_wrapper->stream = CAMEL_STREAM (filterstream);
+ data_wrapper->offline = FALSE;
+
+ camel_object_unref (CAMEL_OBJECT (imap_wrapper->folder));
+ imap_wrapper->folder = NULL;
+ g_free (imap_wrapper->uid);
+ imap_wrapper->uid = NULL;
+ g_free (imap_wrapper->part_spec);
+ imap_wrapper->part = NULL;
+
+ return parent_class->write_to_stream (data_wrapper, stream);
+
+ lose:
+ errno = ENETUNREACH;
+ return -1;
+}
+
+
+CamelDataWrapper *
+camel_imap_wrapper_new (CamelFolder *folder, CamelContentType *type,
+ const char *uid, const char *part_spec,
+ CamelMimePart *part)
+{
+ CamelImapWrapper *imap_wrapper;
+
+ 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->uid = g_strdup (uid);
+ imap_wrapper->part_spec = g_strdup (part_spec);
+
+ /* Don't ref this, it's our parent. */
+ imap_wrapper->part = part;
+
+ return (CamelDataWrapper *)imap_wrapper;
+}
diff --git a/camel/providers/imap/camel-imap-wrapper.h b/camel/providers/imap/camel-imap-wrapper.h
new file mode 100644
index 0000000000..34b28424b7
--- /dev/null
+++ b/camel/providers/imap/camel-imap-wrapper.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-imap-wrapper.h: data wrapper for offline IMAP data */
+
+/*
+ * Copyright 2000 Helix Code, Inc. (http://www.helixcode.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_WRAPPER_H
+#define CAMEL_IMAP_WRAPPER_H 1
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-data-wrapper.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))
+#define CAMEL_IMAP_WRAPPER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAP_WRAPPER_TYPE, CamelImapWrapperClass))
+#define CAMEL_IS_IMAP_WRAPPER(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAP_WRAPPER_TYPE))
+
+typedef struct
+{
+ CamelDataWrapper parent_object;
+
+ CamelFolder *folder;
+ char *uid, *part_spec;
+ CamelMimePart *part;
+} CamelImapWrapper;
+
+typedef struct {
+ CamelDataWrapperClass parent_class;
+
+} CamelImapWrapperClass;
+
+/* Standard Camel function */
+CamelType camel_imap_wrapper_get_type (void);
+
+/* Constructor */
+CamelDataWrapper *camel_imap_wrapper_new (CamelFolder *folder,
+ CamelContentType *type,
+ const char *uid,
+ const char *part_spec,
+ CamelMimePart *part);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_DATA_WRAPPER_H */