From 8ad855fef6632e32723242fda554fce04f025036 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 17 Jan 2001 00:27:19 +0000 Subject: 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 --- camel/providers/imap/camel-imap-wrapper.c | 212 ++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 camel/providers/imap/camel-imap-wrapper.c (limited to 'camel/providers/imap/camel-imap-wrapper.c') 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 + * + * 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 + +#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 +#include + +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; +} -- cgit v1.2.3