aboutsummaryrefslogtreecommitdiffstats
path: root/camel
diff options
context:
space:
mode:
Diffstat (limited to 'camel')
-rw-r--r--camel/ChangeLog301
-rw-r--r--camel/Makefile.am22
-rw-r--r--camel/camel-folder-pt-proxy.c20
-rw-r--r--camel/camel-folder-summary.h120
-rw-r--r--camel/camel-folder.c150
-rw-r--r--camel/camel-folder.h75
-rw-r--r--camel/camel-mime-filter-basic.c219
-rw-r--r--camel/camel-mime-filter-basic.h60
-rw-r--r--camel/camel-mime-filter-charset.c243
-rw-r--r--camel/camel-mime-filter-charset.h54
-rw-r--r--camel/camel-mime-filter-index.c166
-rw-r--r--camel/camel-mime-filter-index.h57
-rw-r--r--camel/camel-mime-filter-save.c160
-rw-r--r--camel/camel-mime-filter-save.h53
-rw-r--r--camel/camel-mime-filter.c227
-rw-r--r--camel/camel-mime-filter.h84
-rw-r--r--camel/camel-mime-parser.c1199
-rw-r--r--camel/camel-mime-parser.h109
-rw-r--r--camel/camel-mime-part.c84
-rw-r--r--camel/camel-mime-part.h3
-rw-r--r--camel/camel-mime-utils.c1870
-rw-r--r--camel/camel-mime-utils.h105
-rw-r--r--camel/camel-stream-b64.c581
-rw-r--r--camel/camel-stream-b64.h135
-rw-r--r--camel/camel-stream-filter.c314
-rw-r--r--camel/camel-stream-filter.h55
-rw-r--r--camel/camel-stream.c42
-rw-r--r--camel/camel-stream.h3
-rw-r--r--camel/gmime-base64.c106
-rw-r--r--camel/gmime-base64.h16
-rw-r--r--camel/gmime-content-field.c228
-rw-r--r--camel/gmime-content-field.h12
-rw-r--r--camel/gmime-rfc2047.c491
-rw-r--r--camel/gmime-rfc2047.h52
-rw-r--r--camel/providers/Makefile.am2
-rw-r--r--camel/providers/mbox/Makefile.am6
-rw-r--r--camel/providers/mbox/camel-mbox-folder.c543
-rw-r--r--camel/providers/mbox/camel-mbox-folder.h7
-rw-r--r--camel/providers/mbox/camel-mbox-search.c35
-rw-r--r--camel/providers/mbox/camel-mbox-summary.c1556
-rw-r--r--camel/providers/mbox/camel-mbox-summary.h115
-rw-r--r--camel/providers/mbox/camel-mbox-utils.c52
42 files changed, 7068 insertions, 2664 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index e373337fe4..1afbf83876 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,3 +1,24 @@
+2000-04-20 NotZed <NotZed@HelixCode.com>
+
+ * MERGE NEW_PARSER branch into HEAD, fixed conflicts.
+
+ * gmime-content-field.c (_print_parameter): Duh, removed again
+ (@@#$@ cvs merge).
+
+ * camel-mime-utils.c (header_content_type_is): Constify.
+ (header_content_type_unref): Killed a couple warnings.
+
+ * camel-folder.c (_init): Removed more log crap.
+
+ * providers/Makefile.am (SUBDIRS): Removed nntp, pending fixes for
+ summary changes.
+
+ * providers/mbox/camel-mbox-folder.c (_get_message_by_number):
+ Fixed for new summary interface. Added a warning for using this
+ broken api.
+ (_get_message_by_uid): Fixed for message new with session
+ vanishing.
+
2000-04-19 Dan Winship <danw@helixcode.com>
* camel-simple-data-wrapper-stream.c
@@ -141,6 +162,277 @@
* providers/Makefile.am: Readded smtp now that smtp builds without
error.
+2000-04-20 NotZed <NotZed@HelixCode.com>
+
+ * providers/mbox/camel-mbox-summary.c
+ (camel_mbox_summary_next_uid): Public function to get the next
+ uid, makes sure its saved to disk too.
+
+ * camel-mime-part.c (my_finalize): Fix disposition crap with a
+ real disposition.
+ (my_set_disposition): Likewise.
+ (my_get_disposition): And here.
+ (my_write_to_stream): And here, needs more cleanup.
+
+ * providers/mbox/camel-mbox-folder.c (_append_message): Assign a
+ new uid at this point.
+
+ * gmime-content-field.c (gmime_content_field_write_to_stream):
+ Make something up if we have an invalid/missing content type
+ (i.e. text/plain).
+
+2000-04-19 NotZed <NotZed@HelixCode.com>
+
+ * providers/mbox/camel-mbox-folder.c (_delete): Fixed completely
+ broken switch() syntax, only compiled because errno is a macro on
+ some systems.
+ (_list_subfolders): Likewise.
+
+2000-04-18 NotZed <NotZed@HelixCode.com>
+
+ * camel-mime-parser.c (folder_scan_init): init stream to null.
+
+ * providers/mbox/camel-mbox-summary.c
+ (CAMEL_MBOX_SUMMARY_VERSION): Moved to .c file, incremented.
+ (index_folder): Changed to have index passed via the summary.
+ (decode_string): Do a sanity check on the string size, so we dont
+ visit g_malloc()'s friendly abort().
+
+ * camel-folder-pt-proxy.c (camel_folder_pt_proxy_class_init):
+ Removed reference to set_name.
+ (_set_name): Removed.
+
+ * providers/mbox/camel-mbox-utils.c
+ (parsed_information_to_mbox_summary): Removed. Most of this file
+ is about to be binned.
+
+ * providers/mbox/camel-mbox-search.c (func_header_contains): Fixes
+ for changes to summary interface.
+ (struct _searchcontext): Remove pointer to message info, get it
+ straight from the mboxsummary.
+ (camel_mbox_folder_search_by_expression): New summary interface.
+ (camel_mbox_folder_search_by_expression): Uh, the summary is not
+ an object anymore (well not yet).
+
+ * providers/mbox/camel-mbox-folder.c
+ (camel_mbox_folder_class_init): Removed set_name init.
+ (_set_name): Removed.
+ (_open): Call new summary interface.
+ (_close): Use new summary interface.
+ (_create): Removed a summary object leak.
+ (_get_message_count): New summary interface.
+ (_get_uid_list): Use new summary interface. FIXME: this is leaky.
+ (_get_message_by_uid): Use the new summary interface, some
+ cleanup.
+ (_append_message): Totally changed, basically just appends the
+ message directly, ignores the summary (for now), the summary will
+ fix itself up if it needs to.
+ (_check_get_or_maybe_generate_summary_file): Bye bye old code.
+ (summary_get_message_info): Implement get_message_info again, for
+ folder.
+
+ * camel-folder.c (camel_folder_class_init): Removed set_name
+ setup.
+ (_set_name): Moved contents into _init.
+ (_init): Perform the old functions of set_name here.
+
+ * camel-folder.h: Removed the set_name internal interface.
+
+2000-04-14 NotZed <NotZed@HelixCode.com>
+
+ * providers/mbox/camel-mbox-summary.[ch]: Completely replaced with
+ new code.
+
+ * Makefile.am (libcamel_la_SOURCES): Removed
+ camel-folder-summary.[ch].
+
+ * camel-folder.h (struct _CamelFolder): Removed summary.
+ (struct _CamelFolder): Changed flags to be 1 bit bitfields.
+
+ * camel-folder-summary.[ch]: Class removed entirely.
+
+ * camel-folder.c (camel_folder_get_summary): Removed.
+ (camel_folder_summary_get_message_info): Moved from
+ camel-folder-summary.c
+ (camel_folder_summary_get_subfolder_info): Moved from
+ camel-folder-summary.c
+
+ * camel-mime-parser.c (folder_scan_step): Store the start of
+ headers and start of from in the scan state.
+ (camel_mime_parser_tell_start_headers): Query the start of the
+ headers.
+ (camel_mime_parser_tell_start_from): Query the cached start of
+ from marker.
+
+2000-04-13 NotZed <NotZed@HelixCode.com>
+
+ * gmime-content-field.c (gmime_content_field_free): Removed this
+ function. If its too dangerous to use, it shouldn't be here.
+ (gmime_content_field_ref): Also ref the embedded content-type.
+ (gmime_content_field_unref): Ditto to unref it.
+
+ * camel-mime-utils.h: Add a refcount for content-type header.
+
+ * camel-mime-utils.c (header_content_type_unref): Implement unref
+ for content-type.
+ (header_content_type_ref): Implement ref for header content type.
+
+2000-04-12 NotZed <NotZed@HelixCode.com>
+
+ * gmime-content-field.h: Changed to use a _header_content_type.
+ Added type/subtype back for compatability with clients.
+
+ * gmime-content-field.c: Basically a total rewrite, and now just a
+ thin wrapper ontop of header_content_type.
+ (_free_parameter): Got rid of it.
+ (gmime_content_field_new): Use header_content_type_* functions.
+ (gmime_content_field_set_parameter): Likewise.
+ (_print_parameter): Blow away.
+ (gmime_content_field_write_to_stream): Get details from the
+ content_type field. Should check if it needs to escape chars in
+ the paramter value.
+ (gmime_content_field_get_mime_type): Likewise.
+ (___debug_print_parameter): Get rid of this rather annoyingly
+ named function.
+ (gmime_content_field_get_parameter): Simplified function.
+ (gmime_content_field_construct_from_string): Fixed this to use a
+ real parser.
+ (gmime_content_field_is_type): New function to test if a type matches.
+ (gmime_content_field_construct_from_string): Track type/subtype
+ from subordinate content_type header struct.
+
+ * gmime-rfc2047.[ch]: Removed. Unused.
+
+ * camel-stream-b64.[ch]: Blown away more duplicated code.
+
+ * Makefile.am: Removed camel-stream-b64.[ch], and
+ gmime-base64.[ch].
+
+ * camel-mime-part.c (my_get_content_object): Replaced
+ camel-stream-b64 with camel-stream-filter/camel-mime-filter-basic.
+ (my_write_content_to_stream): Replaced camel-stream-b64 with the
+ camel-stream-filter with an encoder.
+ (my_get_content_object): Also implement quoted-printable decoding.
+ (my_write_content_to_stream): Also implement quoted-printable
+ encoding.
+ (my_get_output_stream): Took out stream-b64 code (nothing's being
+ executed yet anyway).
+
+ * gmime-base64.[ch]: Blown away. Not used, dont need it.
+
+ * camel-mime-utils.h: Added offset for this header. Records where
+ it is in the source.
+
+ * camel-mime-utils.c (header_raw_append_parse): Add offset
+ parameter, to store where the header is stored in the stream.
+ (header_raw_append): Added offset param.
+ (header_raw_find): Return offset, if a pointer supplied for it.
+ (header_raw_replace): Add offset param.
+ (header_content_type_new): New function, to create an empty
+ content type.
+ (header_content_type_set_param): Set a parameter in the
+ content-type.
+ (header_set_param): Generic header parameter setting function.
+ (header_decode_string): Handle NULL input.
+
+ * camel-mime-parser.c (camel_mime_parser_headers_raw): New
+ function to get access to all the raw headers.
+ (folder_scan_header): Keep track of the header start position, and
+ store it when saving the header.
+
+2000-04-11 NotZed <NotZed@HelixCode.com>
+
+ * camel-mime-utils.c: Moved a bunch of printf's to debug.
+
+ * camel-mime-parser.c: Moved a bunch of printf's to debug.
+ (folder_scan_header): Detect end of each header line using the
+ last scanned char, and not the last scanned position.
+
+ * camel-mime-filter-index.[ch]: Indexing filter. Indexes unicode
+ sequences into ibex files.
+
+2000-04-09 NotZed <NotZed@HelixCode.com>
+
+ * camel-mime-part.c: Dont include gmime-base64.h
+
+ * camel-mime-filter-charset.c (complete): Implement the completion
+ function.
+
+ * camel-mime-parser.c (folder_scan_step): If we get to the end of
+ the body data, check any filters for outstanding completion data.
+ (camel_mime_parser_scan_from): Set whether we scan for "From "
+ headers or not.
+
+ * camel-stream-filter.c (do_read): If we get to end of stream on
+ the source, then call the filtering completion function to see if
+ we have any more data to return.
+
+ * camel-mime-filter-basic.c (filter): Implement quoted printable
+ encoding and decoding filters.
+ (complete): And the complete function as well.
+
+ * camel-mime-utils.c (base64_encode_close): Also take an input
+ buffer, allow closing of filters.
+ (quoted_encode_step): First cut, simple quoted-printable encoder.
+ Doesn't handle trailing spaces/tabs on end of line properly yet.
+ (quoted_encode_close): Complete a quoted-encoding.
+ (is_qpsafe): New type check, for quoted-printable safe characters
+ (that do not need encoding). Thats all bits used in the type
+ table! Rebuilt the types table.
+ (header_content_type_is): Checks a content type against at
+ type/subtype match.
+ (header_content_type_param): Handle NULL content type pointer.
+
+2000-04-08 NotZed <NotZed@HelixCode.com>
+
+ * camel-mime-filter-basic.c (filter): Implement the base64
+ encoder. Problem is, there is no way to know when to close it.
+ Close/Reset will have to provide the same args as filter, so it can
+ flush remaining data *sigh*
+
+ * camel-mime-utils.c (base64_encode_step): A rather complex base64
+ encoder, fast?
+ (base64_step_close): Companion function to finish off the base64
+ sequence.
+
+ * camel-mime-part.c (my_write_content_to_stream): Changed to use
+ camel_stream_write_to_stream().
+
+ * camel-stream.[ch] (camel_stream_write_to_stream): From
+ camel_stream_b64_write_to_stream(). Fixed some infinite loop
+ bugs with error conditions.
+
+ * camel-stream-b64.[ch] (camel_stream_b64_write_to_stream): Removed.
+ This has nothing to do with stream-b64, so i've moved it to
+ CamelStream.
+
+ * camel-mime-utils.h: Add a comment about refcounting
+ header_content_type struct.
+
+ * Makefile.am: Added camel-stream-filter*.[ch].
+
+ * camel-stream-filter.[ch]: Class to implement a generic
+ (multipass) filter ontop of a stream. Only implements a read-only
+ stream.
+
+ * camel-mime-parser.c (camel_mime_parser_filter_add): Ref the
+ filter we just added.
+
+ * Makefile.am: Added camel-mime-filter*.[ch].
+
+ * camel-mime-filter-charset.[ch]: A filter to preform character set
+ conversion (uses unicode_iconv).
+
+ * camel-mime-filter-save.[ch]: A simple filter which will save all
+ data directly to a file or file descriptor.
+
+ * camel-mime-filter-basic.[ch]: Implements the basic mime filters,
+ base64 and quoted-printable decoding (encoding not implemented yet).
+
+ * camel-mime-filter.[ch]: A filtering class, which can filter streams
+ of data without having to copy them. Simpler than stream classes,
+ and can be plugged into a single stream class (when i write it).
+
2000-04-07 Dan Winship <danw@helixcode.com>
* providers/pop3/camel-pop3-store.c (pop3_connect): Clarify error
@@ -151,6 +443,15 @@
2000-04-07 NotZed <NotZed@HelixCode.com>
+ * Makefile.am: Added camel-mime-parser/camel-mime-utils.
+
+ * camel-mime-parser.c: Fast mime parser.
+
+ * camel-mime-utils.c: Mime utility functions, and email header
+ parsers.
+
+2000-04-07 NotZed <NotZed@HelixCode.com>
+
* providers/Makefile.am: Removed smtp for now, its a long way from
building.
* providers/smtp/Makefile.in: Removed file that shouldn't have been
diff --git a/camel/Makefile.am b/camel/Makefile.am
index fbc98cea86..5e8ea1e2d4 100644
--- a/camel/Makefile.am
+++ b/camel/Makefile.am
@@ -39,7 +39,6 @@ libcamel_la_SOURCES = \
camel-simple-data-wrapper-stream.c \
camel-stream-data-wrapper.c \
camel-folder.c \
- camel-folder-summary.c \
camel-folder-utils.c \
camel-medium.c \
camel-marshal-utils.c \
@@ -58,20 +57,26 @@ libcamel_la_SOURCES = \
camel-session.c \
camel-store.c \
camel-stream.c \
- camel-stream-b64.c \
camel-stream-buffer.c \
camel-stream-fs.c \
camel-stream-mem.c \
camel-transport.c \
camel-url.c \
data-wrapper-repository.c \
- gmime-base64.c \
gmime-content-field.c \
gmime-utils.c \
gstring-util.c \
hash-table-utils.c \
md5-utils.c \
string-utils.c \
+ camel-mime-parser.c \
+ camel-mime-utils.c \
+ camel-mime-filter.c \
+ camel-mime-filter-basic.c \
+ camel-mime-filter-save.c \
+ camel-mime-filter-charset.c \
+ camel-mime-filter-index.c \
+ camel-stream-filter.c \
$(pthread_SRC)
libcamelinclude_HEADERS = \
@@ -82,7 +87,6 @@ libcamelinclude_HEADERS = \
camel-simple-data-wrapper-stream.h \
camel-stream-data-wrapper.h \
camel-folder.h \
- camel-folder-summary.h \
camel-folder-utils.h \
camel-mime-body-part.h \
camel-marshal-utils.h \
@@ -101,14 +105,12 @@ libcamelinclude_HEADERS = \
camel-session.h \
camel-store.h \
camel-stream.h \
- camel-stream-b64.h \
camel-stream-buffer.h \
camel-stream-fs.h \
camel-stream-mem.h \
camel-transport.h \
camel-url.h \
data-wrapper-repository.h \
- gmime-base64.h \
gmime-content-field.h \
gmime-utils.h \
gstring-util.h \
@@ -116,6 +118,14 @@ libcamelinclude_HEADERS = \
md5-utils.h \
string-utils.h \
camel-exception-list.def \
+ camel-mime-parser.h \
+ camel-mime-utils.h \
+ camel-mime-filter.h \
+ camel-mime-filter-basic.h \
+ camel-mime-filter-save.h \
+ camel-mime-filter-charset.h \
+ camel-mime-filter-index.h \
+ camel-stream-filter.h \
$(pthread_HDR)
libcamel_extra_sources = \
diff --git a/camel/camel-folder-pt-proxy.c b/camel/camel-folder-pt-proxy.c
index cbf382c6ed..8d8fda4ccf 100644
--- a/camel/camel-folder-pt-proxy.c
+++ b/camel/camel-folder-pt-proxy.c
@@ -120,7 +120,6 @@ camel_folder_pt_proxy_class_init (CamelFolderPtProxyClass *camel_folder_pt_proxy
camel_folder_class->open_async = _open_async;
camel_folder_class->close_async = _close_async;
#endif
- camel_folder_class->set_name = _set_name;
camel_folder_class->get_name = _get_name;
camel_folder_class->can_hold_folders = _can_hold_folders;
camel_folder_class->can_hold_messages = _can_hold_messages;
@@ -475,25 +474,6 @@ static void _close (CamelFolder *folder,
}
-
-
-
-/* folder->set_name implementation */
-
-static void
-_set_name (CamelFolder *folder,
- const gchar *name,
- CamelException *ex)
-{
- CamelFolderPtProxy *proxy_folder;
-
- proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
- CF_CLASS (proxy_folder->real_folder)->
- set_name (proxy_folder->real_folder, name, ex);
-
-}
-
-
/* folder->get_name implementation */
/* this one is not executed in a thread */
static const gchar *
diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h
deleted file mode 100644
index 720e09118f..0000000000
--- a/camel/camel-folder-summary.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* camelFolderSummary.h : Abstract class for a folder summary */
-
-/*
- *
- * Author :
- * Bertrand Guiheneuf <bertrand@helixcode.com>
- *
- * Copyright 1999, 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_FOLDER_SUMMARY_H
-#define CAMEL_FOLDER_SUMMARY_H 1
-
-
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus }*/
-
-#include <gtk/gtk.h>
-#include "camel-types.h"
-
-
-#define CAMEL_FOLDER_SUMMARY_TYPE (camel_folder_summary_get_type ())
-#define CAMEL_FOLDER_SUMMARY(obj) (GTK_CHECK_CAST((obj), CAMEL_FOLDER_SUMMARY_TYPE, CamelFolderSummary))
-#define CAMEL_FOLDER_SUMMARY_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_FOLDER_SUMMARY_TYPE, CamelFolderSummaryClass))
-#define CAMEL_IS_FOLDER_SUMMARY(o) (GTK_CHECK_TYPE((o), CAMEL_FOLDER_SUMMARY_TYPE))
-
-
-
-
-typedef struct {
- gchar *name;
- gint nb_message;
- gint nb_unread_message;
- gint nb_deleted_message;
-} CamelFolderInfo;
-
-
-
-
-typedef struct {
-
- /* all the followings are public fields */
- gchar *subject;
-
- gchar *sender;
- gchar *to;
-
- gchar *sent_date;
- gchar *received_date;
-
- guint32 size;
- gchar *uid;
-} CamelMessageInfo;
-
-
-
-
-struct _CamelFolderSummary {
- GtkObject parent_object;
-
-};
-
-
-
-
-typedef struct {
- GtkObjectClass parent_class;
-
- /* Virtual methods */
- int (*count_messages) (CamelFolderSummary *summary);
- int (*count_subfolders) (CamelFolderSummary *summary);
-
- GPtrArray * (*get_subfolder_info) (CamelFolderSummary *summary,
- int first, int count);
- GPtrArray * (*get_message_info) (CamelFolderSummary *summary,
- int first, int count);
-
-} CamelFolderSummaryClass;
-
-
-
-/* Standard Gtk function */
-GtkType camel_folder_summary_get_type (void);
-
-
-/* public methods */
-int camel_folder_summary_count_messages (CamelFolderSummary *summary);
-int camel_folder_summary_count_subfolders (CamelFolderSummary *summary);
-
-GPtrArray *camel_folder_summary_get_subfolder_info (CamelFolderSummary *summary,
- int first, int count);
-GPtrArray *camel_folder_summary_get_message_info (CamelFolderSummary *summary,
- int first, int count);
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* CAMEL_FOLDER_SUMMARY_H */
diff --git a/camel/camel-folder.c b/camel/camel-folder.c
index 58a2985399..31141da5c9 100644
--- a/camel/camel-folder.c
+++ b/camel/camel-folder.c
@@ -66,9 +66,6 @@ static void _close_async (CamelFolder *folder,
#endif
-static void _set_name (CamelFolder *folder,
- const gchar *name,
- CamelException *ex);
static const gchar *_get_name (CamelFolder *folder);
static const gchar *_get_full_name (CamelFolder *folder);
@@ -134,7 +131,8 @@ static void _delete_message_by_uid (CamelFolder *folder,
const gchar *uid,
CamelException *ex);
-
+static GPtrArray *get_message_info (CamelFolder *folder, int first, int count);
+static GPtrArray *get_subfolder_info (CamelFolder *folder, int first, int count);
static void
@@ -154,7 +152,6 @@ camel_folder_class_init (CamelFolderClass *camel_folder_class)
#ifdef FOLDER_ASYNC_TEST
camel_folder_class->close_async = _close_async;
#endif
- camel_folder_class->set_name = _set_name;
camel_folder_class->get_name = _get_name;
camel_folder_class->get_full_name = _get_full_name;
camel_folder_class->can_hold_folders = _can_hold_folders;
@@ -182,6 +179,9 @@ camel_folder_class_init (CamelFolderClass *camel_folder_class)
camel_folder_class->delete_message_by_uid = _delete_message_by_uid;
camel_folder_class->get_uid_list = _get_uid_list;
+ camel_folder_class->get_subfolder_info = get_subfolder_info;
+ camel_folder_class->get_message_info = get_message_info;
+
/* virtual method overload */
gtk_object_class->finalize = _finalize;
}
@@ -251,6 +251,9 @@ _init (CamelFolder *folder, CamelStore *parent_store,
CamelFolder *parent_folder, const gchar *name,
gchar separator, CamelException *ex)
{
+ gchar *full_name;
+ const gchar *parent_full_name;
+
g_assert (folder != NULL);
g_assert (parent_store != NULL);
g_assert (folder->parent_store == NULL);
@@ -265,7 +268,28 @@ _init (CamelFolder *folder, CamelStore *parent_store,
folder->open_mode = FOLDER_OPEN_UNKNOWN;
folder->open_state = FOLDER_CLOSE;
folder->separator = separator;
- CF_CLASS (folder)->set_name (folder, name, ex);
+
+ /* if the folder already has a name, free it */
+ g_free (folder->name);
+ g_free (folder->full_name);
+
+ /* set those fields to NULL now, so that if an
+ exception occurs, they will be set anyway */
+ folder->name = NULL;
+ folder->full_name = NULL;
+
+ if (folder->parent_folder) {
+ parent_full_name =
+ camel_folder_get_full_name (folder->parent_folder);
+
+ full_name = g_strdup_printf ("%s%c%s", parent_full_name,
+ folder->separator, name);
+ } else {
+ full_name = g_strdup_printf ("%c%s", folder->separator, name);
+ }
+
+ folder->name = g_strdup (name);
+ folder->full_name = full_name;
}
@@ -431,44 +455,6 @@ camel_folder_close_async (CamelFolder *folder,
#endif
-static void
-_set_name (CamelFolder *folder,
- const gchar *name,
- CamelException *ex)
-{
- gchar *full_name;
- const gchar *parent_full_name;
-
- g_assert (folder->parent_store != NULL);
- g_assert (name != NULL);
- g_assert (!camel_folder_is_open (folder));
-
- /* if the folder already has a name, free it */
- g_free (folder->name);
- g_free (folder->full_name);
-
- /* set those fields to NULL now, so that if an
- exception occurs, they will be set anyway */
- folder->name = NULL;
- folder->full_name = NULL;
-
- if (folder->parent_folder) {
- parent_full_name =
- camel_folder_get_full_name (folder->parent_folder);
-
- full_name = g_strdup_printf ("%s%c%s", parent_full_name,
- folder->separator, name);
- } else {
- full_name = g_strdup_printf ("%c%s", folder->separator, name);
- }
-
- folder->name = g_strdup (name);
- folder->full_name = full_name;
-
-}
-
-
-
static const gchar *
_get_name (CamelFolder *folder)
{
@@ -1204,42 +1190,72 @@ camel_folder_copy_message_to (CamelFolder *folder,
CF_CLASS (folder)->copy_message_to (folder, message, dest_folder, ex);;
}
-
-
-
-
/* summary stuff */
+static GPtrArray *
+get_subfolder_info (CamelFolder *folder, int first, int count)
+{
+ g_warning ("CamelFolder::get_subfolder_info not implemented for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
+ return NULL;
+}
-gboolean
-camel_folder_has_summary_capability (CamelFolder *folder)
+/**
+ * camel_folder_summary_get_subfolder_info: return an array of subfolders
+ * @summary: a summary
+ * @first: the index of the first subfolder to return information for
+ * (starting from 0)
+ * @count: the number of subfolders to return information for
+ *
+ * Returns an array of pointers to CamelFolderInfo objects. The caller
+ * must free the array when it is done with it, but should not modify
+ * the elements.
+ *
+ * Return value: an array containing information about the subfolders.
+ **/
+GPtrArray *
+camel_folder_summary_get_subfolder_info (CamelFolder *folder,
+ int first, int count)
{
g_assert (folder != NULL);
- return folder->has_summary_capability;
+ return CF_CLASS (folder)->get_subfolder_info (folder, first, count);
}
+static GPtrArray *
+get_message_info (CamelFolder *folder, int first, int count)
+{
+ g_warning ("CamelFolder::get_message_info not implemented for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
+ return NULL;
+}
+
/**
- * camel_folder_get_summary: return the summary of a folder
- * @folder: folder object
- * @ex: exception object
- *
- * Return a CamelFolderSummary object from
- * which the main informations about a folder
- * can be retrieved.
- *
- * Return value: the folder summary object.
+ * camel_folder_summary_get_message_info: return an array of messages
+ * @folder: a camel folder
+ * @first: the index of the first message to return information for
+ * (starting from 0)
+ * @count: the number of messages to return information for
+ *
+ * Returns an array of pointers to CamelMessageInfo objects. The caller
+ * must free the array when it is done with it, but should not modify
+ * the elements.
+ *
+ * Return value: an array containing information about the messages.
**/
-CamelFolderSummary *
-camel_folder_get_summary (CamelFolder *folder,
- CamelException *ex)
+GPtrArray *
+camel_folder_summary_get_message_info (CamelFolder *folder,
+ int first, int count)
{
g_assert (folder != NULL);
- g_assert (camel_folder_is_open (folder));
-
- return folder->summary;
+ return CF_CLASS (folder)->get_message_info (folder, first, count);
}
-
+/* summary stuff */
+/* TODO: is this function required anyway? */
+gboolean
+camel_folder_has_summary_capability (CamelFolder *folder)
+{
+ g_assert (folder != NULL);
+ return folder->has_summary_capability;
+}
/* UIDs stuff */
diff --git a/camel/camel-folder.h b/camel/camel-folder.h
index 9a717e3db6..b8b2db0da5 100644
--- a/camel/camel-folder.h
+++ b/camel/camel-folder.h
@@ -35,6 +35,7 @@ extern "C" {
#endif /* __cplusplus }*/
#include <gtk/gtk.h>
+#include <time.h>
#include "camel-types.h"
#define CAMEL_FOLDER_TYPE (camel_folder_get_type ())
@@ -58,12 +59,56 @@ typedef enum {
typedef void (*CamelFolderAsyncCallback) ();
typedef void (CamelSearchFunc)(CamelFolder *folder, int id, gboolean complete, GList *matches, void *data);
+/* these structs from camel-folder-summary.h ... (remove comment after cleanup soon) */
+/* TODO: perhaps they should be full-block objects? */
+/* FIXME: rename this to something more suitable */
+typedef struct {
+ gchar *name;
+ gint nb_message; /* ick, these should be renamed to something better */
+ gint nb_unread_message;
+ gint nb_deleted_message;
+} CamelFolderInfo;
+
+/* A tree of message content info structures
+ describe the content structure of the message (if it has any) */
+typedef struct _CamelMessageContentInfo {
+ struct _CamelMessageContentInfo *next;
+
+ struct _CamelMessageContentInfo *childs;
+ struct _CamelMessageContentInfo *parent;
+
+ struct _header_content_type *type;
+ char *id;
+ char *description;
+ char *encoding;
+
+ guint32 size;
+} CamelMessageContentInfo;
+
+/* TODO: rename this?? */
+/* TODO: Make this an object, maybe? */
+typedef struct {
+ /* public fields */
+ gchar *subject;
+ gchar *to;
+ gchar *from;
+
+ gchar *uid;
+ guint32 flags;
+
+
+ time_t date_sent;
+ time_t date_received;
+
+ /* tree of content description */
+ CamelMessageContentInfo *content;
+} CamelMessageInfo;
+
+
struct _CamelFolder
{
GtkObject parent_object;
- gboolean can_hold_folders;
- gboolean can_hold_messages;
CamelFolderOpenMode open_mode;
CamelFolderState open_state;
gchar *name;
@@ -73,12 +118,11 @@ struct _CamelFolder
CamelFolder *parent_folder;
GList *permanent_flags;
- gboolean has_summary_capability;
- CamelFolderSummary *summary;
-
- gboolean has_uid_capability;
-
- gboolean has_search_capability;
+ gboolean can_hold_folders:1;
+ gboolean can_hold_messages:1;
+ gboolean has_summary_capability:1;
+ gboolean has_uid_capability:1;
+ gboolean has_search_capability:1;
};
@@ -111,10 +155,6 @@ typedef struct {
gpointer user_data,
CamelException *ex);
- void (*set_name) (CamelFolder *folder,
- const gchar *name,
- CamelException *ex);
-
const gchar * (*get_name) (CamelFolder *folder);
const gchar * (*get_full_name) (CamelFolder *folder);
@@ -206,6 +246,10 @@ typedef struct {
gboolean (*search_complete)(CamelFolder *folder, int searchid, gboolean wait, CamelException *ex);
void (*search_cancel) (CamelFolder *folder, int searchid, CamelException *ex);
+ /* moved the old summary stuff from camel-folder-summary.h here */
+ GPtrArray * (*get_subfolder_info) (CamelFolder *, int first, int count);
+ GPtrArray * (*get_message_info) (CamelFolder *, int first, int count);
+
} CamelFolderClass;
@@ -316,6 +360,13 @@ int camel_folder_search_by_expression(CamelFolder *folder, const char *expr
gboolean camel_folder_search_complete(CamelFolder *folder, int searchid, gboolean wait, CamelException *ex);
void camel_folder_search_cancel(CamelFolder *folder, int searchid, CamelException *ex);
+/* summary info, from the old camel-folder-summary
+ FIXME: rename these slightly? */
+GPtrArray *camel_folder_summary_get_subfolder_info (CamelFolder *summary,
+ int first, int count);
+GPtrArray *camel_folder_summary_get_message_info (CamelFolder *summary,
+ int first, int count);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/camel/camel-mime-filter-basic.c b/camel/camel-mime-filter-basic.c
new file mode 100644
index 0000000000..60566abf4e
--- /dev/null
+++ b/camel/camel-mime-filter-basic.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "camel-mime-filter-basic.h"
+
+#include "camel-mime-utils.h"
+
+static void camel_mime_filter_basic_class_init (CamelMimeFilterBasicClass *klass);
+static void camel_mime_filter_basic_init (CamelMimeFilterBasic *obj);
+
+static CamelMimeFilterClass *camel_mime_filter_basic_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_filter_basic_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeFilterBasic",
+ sizeof (CamelMimeFilterBasic),
+ sizeof (CamelMimeFilterBasicClass),
+ (GtkClassInitFunc) camel_mime_filter_basic_class_init,
+ (GtkObjectInitFunc) camel_mime_filter_basic_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (camel_mime_filter_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ ((GtkObjectClass *)camel_mime_filter_basic_parent)->finalize (o);
+}
+
+/* should this 'flush' outstanding state/data bytes? */
+static void
+reset(CamelMimeFilter *mf)
+{
+ CamelMimeFilterBasic *f = (CamelMimeFilterBasic *)mf;
+
+ f->state = 0;
+ f->save = 0;
+}
+
+static void
+complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ CamelMimeFilterBasic *f = (CamelMimeFilterBasic *)mf;
+ int newlen;
+
+ switch(f->type) {
+ case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
+ /* wont go to more than 2x size (overly conservative) */
+ camel_mime_filter_set_size(mf, len*2, FALSE);
+ newlen = base64_encode_close(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_QP_ENC:
+ /* FIXME: *3 is probably not quite enough ... */
+ camel_mime_filter_set_size(mf, len*3, FALSE);
+ newlen = quoted_encode_close(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
+ /* output can't possibly exceed the input size */
+ camel_mime_filter_set_size(mf, len, FALSE);
+ newlen = base64_decode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_QP_DEC:
+ /* output can't possibly exceed the input size */
+ camel_mime_filter_set_size(mf, len, FALSE);
+ newlen = quoted_decode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ default:
+ g_warning("unknown type %d in CamelMimeFilterBasic", f->type);
+ goto donothing;
+ }
+
+ *out = mf->outbuf;
+ *outlen = newlen;
+ *outprespace = mf->outpre;
+
+ return;
+donothing:
+ *out = in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+/* here we do all of the basic mime filtering */
+static void
+filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ CamelMimeFilterBasic *f = (CamelMimeFilterBasic *)mf;
+ int newlen;
+
+ switch(f->type) {
+ case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
+ /* wont go to more than 2x size (overly conservative) */
+ camel_mime_filter_set_size(mf, len*2, FALSE);
+ newlen = base64_encode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_QP_ENC:
+ /* FIXME: *3 is probably not quite enough ... */
+ camel_mime_filter_set_size(mf, len*3, FALSE);
+ newlen = quoted_encode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
+ /* output can't possibly exceed the input size */
+ camel_mime_filter_set_size(mf, len, FALSE);
+ newlen = base64_decode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ case CAMEL_MIME_FILTER_BASIC_QP_DEC:
+ /* output can't possibly exceed the input size */
+ camel_mime_filter_set_size(mf, len, FALSE);
+ newlen = quoted_decode_step(in, len, mf->outbuf, &f->state, &f->save);
+ break;
+ default:
+ g_warning("unknown type %d in CamelMimeFilterBasic", f->type);
+ goto donothing;
+ }
+
+ *out = mf->outbuf;
+ *outlen = newlen;
+ *outprespace = mf->outpre;
+
+ return;
+donothing:
+ *out = in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+camel_mime_filter_basic_class_init (CamelMimeFilterBasicClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ camel_mime_filter_basic_parent = gtk_type_class (camel_mime_filter_get_type ());
+
+ object_class->finalize = finalise;
+
+ filter_class->reset = reset;
+ filter_class->filter = filter;
+ filter_class->complete = complete;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_filter_basic_init (CamelMimeFilterBasic *obj)
+{
+ obj->state = 0;
+ obj->save = 0;
+}
+
+/**
+ * camel_mime_filter_basic_new:
+ *
+ * Create a new CamelMimeFilterBasic object.
+ *
+ * Return value: A new CamelMimeFilterBasic widget.
+ **/
+CamelMimeFilterBasic *
+camel_mime_filter_basic_new (void)
+{
+ CamelMimeFilterBasic *new = CAMEL_MIME_FILTER_BASIC ( gtk_type_new (camel_mime_filter_basic_get_type ()));
+ return new;
+}
+
+CamelMimeFilterBasic *
+camel_mime_filter_basic_new_type(CamelMimeFilterBasicType type)
+{
+ CamelMimeFilterBasic *new;
+
+ switch (type) {
+ case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
+ case CAMEL_MIME_FILTER_BASIC_QP_ENC:
+ case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
+ case CAMEL_MIME_FILTER_BASIC_QP_DEC:
+ new = camel_mime_filter_basic_new();
+ new->type = type;
+ break;
+ default:
+ g_warning("Invalid type of CamelMimeFilterBasic requested: %d", type);
+ new = NULL;
+ break;
+ }
+ return new;
+}
+
diff --git a/camel/camel-mime-filter-basic.h b/camel/camel-mime-filter-basic.h
new file mode 100644
index 0000000000..57d9898fa6
--- /dev/null
+++ b/camel/camel-mime-filter-basic.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_FILTER_BASIC_H
+#define _CAMEL_MIME_FILTER_BASIC_H
+
+#include <gtk/gtk.h>
+#include <camel/camel-mime-filter.h>
+
+#define CAMEL_MIME_FILTER_BASIC(obj) GTK_CHECK_CAST (obj, camel_mime_filter_basic_get_type (), CamelMimeFilterBasic)
+#define CAMEL_MIME_FILTER_BASIC_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_filter_basic_get_type (), CamelMimeFilterBasicClass)
+#define IS_CAMEL_MIME_FILTER_BASIC(obj) GTK_CHECK_TYPE (obj, camel_mime_filter_basic_get_type ())
+
+typedef struct _CamelMimeFilterBasic CamelMimeFilterBasic;
+typedef struct _CamelMimeFilterBasicClass CamelMimeFilterBasicClass;
+
+typedef enum {
+ CAMEL_MIME_FILTER_BASIC_BASE64_ENC = 1,
+ CAMEL_MIME_FILTER_BASIC_BASE64_DEC,
+ CAMEL_MIME_FILTER_BASIC_QP_ENC,
+ CAMEL_MIME_FILTER_BASIC_QP_DEC,
+} CamelMimeFilterBasicType;
+
+struct _CamelMimeFilterBasic {
+ CamelMimeFilter parent;
+
+ struct _CamelMimeFilterBasicPrivate *priv;
+
+ CamelMimeFilterBasicType type;
+
+ int state;
+ int save;
+};
+
+struct _CamelMimeFilterBasicClass {
+ CamelMimeFilterClass parent_class;
+};
+
+guint camel_mime_filter_basic_get_type (void);
+CamelMimeFilterBasic *camel_mime_filter_basic_new (void);
+CamelMimeFilterBasic *camel_mime_filter_basic_new_type (CamelMimeFilterBasicType type);
+
+#endif /* ! _CAMEL_MIME_FILTER_BASIC_H */
diff --git a/camel/camel-mime-filter-charset.c b/camel/camel-mime-filter-charset.c
new file mode 100644
index 0000000000..eb9db60802
--- /dev/null
+++ b/camel/camel-mime-filter-charset.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <unicode.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include "camel-mime-filter-charset.h"
+
+
+static void camel_mime_filter_charset_class_init (CamelMimeFilterCharsetClass *klass);
+static void camel_mime_filter_charset_init (CamelMimeFilterCharset *obj);
+
+static CamelMimeFilterClass *camel_mime_filter_charset_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_filter_charset_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeFilterCharset",
+ sizeof (CamelMimeFilterCharset),
+ sizeof (CamelMimeFilterCharsetClass),
+ (GtkClassInitFunc) camel_mime_filter_charset_class_init,
+ (GtkObjectInitFunc) camel_mime_filter_charset_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (camel_mime_filter_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)o;
+
+ g_free(f->from);
+ g_free(f->to);
+ if (f->ic != (unicode_iconv_t)-1) {
+ unicode_iconv_close(f->ic);
+ f->ic = (unicode_iconv_t) -1;
+ }
+
+ ((GtkObjectClass *)camel_mime_filter_charset_parent)->finalize (o);
+}
+
+static void
+reset(CamelMimeFilter *mf)
+{
+ CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)mf;
+ char buf[16];
+ char *buffer;
+ int outlen = 16;
+
+ /* what happens with the output bytes if this resets the state? */
+ if (f->ic != (unicode_iconv_t) -1) {
+ buffer = buf;
+ unicode_iconv(f->ic, NULL, 0, &buffer, &outlen);
+ }
+}
+
+static void
+complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace)
+{
+ CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)mf;
+ int converted;
+ char *inbuf, *outbuf;
+ int inlen, outlen;
+
+ if (f->ic == (unicode_iconv_t) -1) {
+ goto donothing;
+ }
+
+ /* FIXME: there's probably a safer way to size this ...? */
+ /* We could always resize if we run out of room in outbuf (but it'd be nice not
+ to have to) */
+ camel_mime_filter_set_size(mf, len*5, FALSE);
+ inbuf = in;
+ inlen = len;
+ outbuf = mf->outbuf;
+ outlen = mf->outsize;
+ if (inlen>0) {
+ converted = unicode_iconv(f->ic, &inbuf, &inlen, &outbuf, &outlen);
+ if (converted == -1) {
+ if (errno != EINVAL) {
+ g_warning("error occured converting: %s", strerror(errno));
+ goto donothing;
+ }
+ }
+
+ if (inlen>0) {
+ g_warning("Output lost in character conversion, invalid sequence encountered?");
+ }
+ }
+
+ /* this 'resets' the output stream, returning back to the initial
+ shift state for multishift charactersets */
+ converted = unicode_iconv(f->ic, NULL, 0, &outbuf, &outlen);
+ if (converted == -1) {
+ g_warning("Conversion failed to complete: %s", strerror(errno));
+ }
+
+ *out = mf->outbuf;
+ *outlenptr = mf->outsize - outlen;
+ *outprespace = mf->outpre;
+ return;
+
+donothing:
+ *out = in;
+ *outlenptr = len;
+ *outprespace = prespace;
+}
+
+static void
+filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace)
+{
+ CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)mf;
+ int converted;
+ char *inbuf, *outbuf;
+ int inlen, outlen;
+
+ if (f->ic == (unicode_iconv_t) -1) {
+ goto donothing;
+ }
+
+ /* FIXME: there's probably a safer way to size this ...? */
+ camel_mime_filter_set_size(mf, len*5, FALSE);
+ inbuf = in;
+ inlen = len;
+ outbuf = mf->outbuf;
+ outlen = mf->outsize;
+ converted = unicode_iconv(f->ic, &inbuf, &inlen, &outbuf, &outlen);
+ if (converted == -1) {
+ if (errno != EINVAL) {
+ g_warning("error occured converting: %s", strerror(errno));
+ goto donothing;
+ }
+ }
+
+ /*
+ NOTE: This assumes EINVAL only occurs because we ran out of
+ bytes for a multibyte sequence, if not, we're in trouble.
+ */
+
+ if (inlen>0) {
+ camel_mime_filter_backup(mf, inbuf, inlen);
+ }
+
+ *out = mf->outbuf;
+ *outlenptr = mf->outsize - outlen;
+ *outprespace = mf->outpre;
+ return;
+
+donothing:
+ *out = in;
+ *outlenptr = len;
+ *outprespace = prespace;
+}
+
+static void
+camel_mime_filter_charset_class_init (CamelMimeFilterCharsetClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ camel_mime_filter_charset_parent = gtk_type_class (camel_mime_filter_get_type ());
+
+ object_class->finalize = finalise;
+
+ filter_class->reset = reset;
+ filter_class->filter = filter;
+ filter_class->complete = complete;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_filter_charset_init (CamelMimeFilterCharset *obj)
+{
+ obj->ic = (unicode_iconv_t)-1;
+}
+
+/**
+ * camel_mime_filter_charset_new:
+ *
+ * Create a new CamelMimeFilterCharset object.
+ *
+ * Return value: A new CamelMimeFilterCharset widget.
+ **/
+CamelMimeFilterCharset *
+camel_mime_filter_charset_new (void)
+{
+ CamelMimeFilterCharset *new = CAMEL_MIME_FILTER_CHARSET ( gtk_type_new (camel_mime_filter_charset_get_type ()));
+ return new;
+}
+
+CamelMimeFilterCharset *
+camel_mime_filter_charset_new_convert(const char *from_charset, const char *to_charset)
+{
+ CamelMimeFilterCharset *new = CAMEL_MIME_FILTER_CHARSET ( gtk_type_new (camel_mime_filter_charset_get_type ()));
+
+ new->ic = unicode_iconv_open(to_charset, from_charset);
+ if (new->ic == (unicode_iconv_t) -1) {
+ g_warning("Cannot create charset conversion from %s to %s: %s", from_charset, to_charset, strerror(errno));
+ gtk_object_unref((GtkObject *)new);
+ new = NULL;
+ } else {
+ new->from = g_strdup(from_charset);
+ new->to = g_strdup(to_charset);
+ }
+ return new;
+}
diff --git a/camel/camel-mime-filter-charset.h b/camel/camel-mime-filter-charset.h
new file mode 100644
index 0000000000..3e15e2e7c7
--- /dev/null
+++ b/camel/camel-mime-filter-charset.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_FILTER_CHARSET_H
+#define _CAMEL_MIME_FILTER_CHARSET_H
+
+#include <gtk/gtk.h>
+#include <camel/camel-mime-filter.h>
+#include <unicode.h>
+
+#define CAMEL_MIME_FILTER_CHARSET(obj) GTK_CHECK_CAST (obj, camel_mime_filter_charset_get_type (), CamelMimeFilterCharset)
+#define CAMEL_MIME_FILTER_CHARSET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_filter_charset_get_type (), CamelMimeFilterCharsetClass)
+#define IS_CAMEL_MIME_FILTER_CHARSET(obj) GTK_CHECK_TYPE (obj, camel_mime_filter_charset_get_type ())
+
+typedef struct _CamelMimeFilterCharset CamelMimeFilterCharset;
+typedef struct _CamelMimeFilterCharsetClass CamelMimeFilterCharsetClass;
+
+struct _CamelMimeFilterCharset {
+ CamelMimeFilter parent;
+
+ struct _CamelMimeFilterCharsetPrivate *priv;
+
+ unicode_iconv_t ic;
+ char *from;
+ char *to;
+};
+
+struct _CamelMimeFilterCharsetClass {
+ CamelMimeFilterClass parent_class;
+};
+
+guint camel_mime_filter_charset_get_type (void);
+CamelMimeFilterCharset *camel_mime_filter_charset_new (void);
+
+CamelMimeFilterCharset *camel_mime_filter_charset_new_convert (const char *from_charset, const char *to_charset);
+
+#endif /* ! _CAMEL_MIME_FILTER_CHARSET_H */
diff --git a/camel/camel-mime-filter-index.c b/camel/camel-mime-filter-index.c
new file mode 100644
index 0000000000..24c223d4e5
--- /dev/null
+++ b/camel/camel-mime-filter-index.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "camel-mime-filter-index.h"
+
+
+static void camel_mime_filter_index_class_init (CamelMimeFilterIndexClass *klass);
+static void camel_mime_filter_index_init (CamelMimeFilterIndex *obj);
+
+static CamelMimeFilterClass *camel_mime_filter_index_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_filter_index_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeFilterIndex",
+ sizeof (CamelMimeFilterIndex),
+ sizeof (CamelMimeFilterIndexClass),
+ (GtkClassInitFunc) camel_mime_filter_index_class_init,
+ (GtkObjectInitFunc) camel_mime_filter_index_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (camel_mime_filter_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ CamelMimeFilterIndex *f = (CamelMimeFilterIndex *)o;
+
+ g_free(f->name);
+ f->index = NULL; /* ibex's need refcounting? */
+
+ ((GtkObjectClass *)camel_mime_filter_index_parent)->finalize (o);
+}
+
+static void
+complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace)
+{
+ CamelMimeFilterIndex *f = (CamelMimeFilterIndex *)mf;
+
+ if (f->index == NULL || f->name==NULL) {
+ goto donothing;
+ }
+
+ ibex_index_buffer(f->index, f->name, in, len, NULL);
+
+donothing:
+ *out = in;
+ *outlenptr = len;
+ *outprespace = prespace;
+}
+
+static void
+filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace)
+{
+ CamelMimeFilterIndex *f = (CamelMimeFilterIndex *)mf;
+ int inleft = 0;
+
+ if (f->index == NULL || f->name==NULL) {
+ goto donothing;
+ }
+
+ ibex_index_buffer(f->index, f->name, in, len, &inleft);
+
+ if (inleft>0) {
+ camel_mime_filter_backup(mf, in+(len-inleft), inleft);
+ }
+
+ *out = in;
+ *outlenptr = len-inleft;
+ *outprespace = prespace;
+ return;
+
+donothing:
+ *out = in;
+ *outlenptr = len;
+ *outprespace = prespace;
+}
+
+static void
+camel_mime_filter_index_class_init (CamelMimeFilterIndexClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ camel_mime_filter_index_parent = gtk_type_class (camel_mime_filter_get_type ());
+
+ object_class->finalize = finalise;
+
+ /*filter_class->reset = reset;*/
+ filter_class->filter = filter;
+ filter_class->complete = complete;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_filter_index_init (CamelMimeFilterIndex *obj)
+{
+}
+
+/**
+ * camel_mime_filter_index_new:
+ *
+ * Create a new CamelMimeFilterIndex object.
+ *
+ * Return value: A new CamelMimeFilterIndex widget.
+ **/
+CamelMimeFilterIndex *
+camel_mime_filter_index_new (void)
+{
+ CamelMimeFilterIndex *new = CAMEL_MIME_FILTER_INDEX ( gtk_type_new (camel_mime_filter_index_get_type ()));
+ return new;
+}
+
+CamelMimeFilterIndex *camel_mime_filter_index_new_ibex (ibex *index)
+{
+ CamelMimeFilterIndex *new = camel_mime_filter_index_new();
+
+ if (new) {
+ new->index = index;
+ new->name = g_strdup("");
+ }
+ return new;
+}
+
+/* Set the match name for any indexed words */
+void camel_mime_filter_index_set_name (CamelMimeFilterIndex *mf, char *name)
+{
+ g_free(mf->name);
+ mf->name = g_strdup(name);
+}
+
+
diff --git a/camel/camel-mime-filter-index.h b/camel/camel-mime-filter-index.h
new file mode 100644
index 0000000000..b4ea1966f0
--- /dev/null
+++ b/camel/camel-mime-filter-index.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_FILTER_INDEX_H
+#define _CAMEL_MIME_FILTER_INDEX_H
+
+#include <gtk/gtk.h>
+#include <libibex/ibex.h>
+
+#include <camel/camel-mime-filter.h>
+
+#define CAMEL_MIME_FILTER_INDEX(obj) GTK_CHECK_CAST (obj, camel_mime_filter_index_get_type (), CamelMimeFilterIndex)
+#define CAMEL_MIME_FILTER_INDEX_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_filter_index_get_type (), CamelMimeFilterIndexClass)
+#define IS_CAMEL_MIME_FILTER_INDEX(obj) GTK_CHECK_TYPE (obj, camel_mime_filter_index_get_type ())
+
+typedef struct _CamelMimeFilterIndex CamelMimeFilterIndex;
+typedef struct _CamelMimeFilterIndexClass CamelMimeFilterIndexClass;
+
+struct _CamelMimeFilterIndex {
+ CamelMimeFilter parent;
+
+ struct _CamelMimeFilterIndexPrivate *priv;
+
+ ibex *index;
+ char *name;
+};
+
+struct _CamelMimeFilterIndexClass {
+ CamelMimeFilterClass parent_class;
+};
+
+guint camel_mime_filter_index_get_type (void);
+CamelMimeFilterIndex *camel_mime_filter_index_new (void);
+
+CamelMimeFilterIndex *camel_mime_filter_index_new_ibex (ibex *);
+
+/* Set the match name for any indexed words */
+void camel_mime_filter_index_set_name (CamelMimeFilterIndex *, char *);
+
+#endif /* ! _CAMEL_MIME_FILTER_INDEX_H */
diff --git a/camel/camel-mime-filter-save.c b/camel/camel-mime-filter-save.c
new file mode 100644
index 0000000000..b5b54a2ee6
--- /dev/null
+++ b/camel/camel-mime-filter-save.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include "camel-mime-filter-save.h"
+
+static void camel_mime_filter_save_class_init (CamelMimeFilterSaveClass *klass);
+static void camel_mime_filter_save_init (CamelMimeFilterSave *obj);
+
+static CamelMimeFilterClass *camel_mime_filter_save_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_filter_save_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeFilterSave",
+ sizeof (CamelMimeFilterSave),
+ sizeof (CamelMimeFilterSaveClass),
+ (GtkClassInitFunc) camel_mime_filter_save_class_init,
+ (GtkObjectInitFunc) camel_mime_filter_save_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (camel_mime_filter_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ CamelMimeFilterSave *f = (CamelMimeFilterSave *)o;
+
+ g_free(f->filename);
+ if (f->fd != -1) {
+ /* FIXME: what do we do with failed writes???? */
+ close(f->fd);
+ }
+
+ ((GtkObjectClass *)camel_mime_filter_save_parent)->finalize (o);
+}
+
+static void
+reset(CamelMimeFilter *mf)
+{
+ CamelMimeFilterSave *f = (CamelMimeFilterSave *)mf;
+
+ /* i dunno, how do you 'reset' a file? reopen it? do i care? */
+ if (f->fd != -1){
+ lseek(f->fd, 0, SEEK_SET);
+ }
+}
+
+/* all this code just to support this little trivial filter! */
+static void
+filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ CamelMimeFilterSave *f = (CamelMimeFilterSave *)mf;
+
+ if (f->fd != -1) {
+ /* FIXME: check return */
+ int outlen = write(f->fd, in, len);
+ if (outlen != len) {
+ g_warning("could not write to '%s': %s", f->filename?f->filename:"<descriptor>", strerror(errno));
+ }
+ }
+ *out = in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+camel_mime_filter_save_class_init (CamelMimeFilterSaveClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ CamelMimeFilterClass *filter_class = (CamelMimeFilterClass *) klass;
+
+ camel_mime_filter_save_parent = gtk_type_class (camel_mime_filter_get_type ());
+
+ object_class->finalize = finalise;
+
+ filter_class->reset = reset;
+ filter_class->filter = filter;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_filter_save_init (CamelMimeFilterSave *f)
+{
+ f->fd = -1;
+}
+
+/**
+ * camel_mime_filter_save_new:
+ *
+ * Create a new CamelMimeFilterSave object.
+ *
+ * Return value: A new CamelMimeFilterSave widget.
+ **/
+CamelMimeFilterSave *
+camel_mime_filter_save_new (void)
+{
+ CamelMimeFilterSave *new = CAMEL_MIME_FILTER_SAVE ( gtk_type_new (camel_mime_filter_save_get_type ()));
+ return new;
+}
+
+CamelMimeFilterSave *
+camel_mime_filter_save_new_name (const char *name, int flags, int mode)
+{
+ CamelMimeFilterSave *new = NULL;
+
+ new = camel_mime_filter_save_new();
+ if (new) {
+ new->fd = open(name, flags, mode);
+ if (new->fd != -1) {
+ new->filename = g_strdup(name);
+ } else {
+ gtk_object_unref((GtkObject *)new);
+ new = NULL;
+ }
+ }
+ return new;
+}
+
diff --git a/camel/camel-mime-filter-save.h b/camel/camel-mime-filter-save.h
new file mode 100644
index 0000000000..970e849db6
--- /dev/null
+++ b/camel/camel-mime-filter-save.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_FILTER_SAVE_H
+#define _CAMEL_MIME_FILTER_SAVE_H
+
+#include <gtk/gtk.h>
+
+#include <camel/camel-mime-filter.h>
+
+#define CAMEL_MIME_FILTER_SAVE(obj) GTK_CHECK_CAST (obj, camel_mime_filter_save_get_type (), CamelMimeFilterSave)
+#define CAMEL_MIME_FILTER_SAVE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_filter_save_get_type (), CamelMimeFilterSaveClass)
+#define IS_CAMEL_MIME_FILTER_SAVE(obj) GTK_CHECK_TYPE (obj, camel_mime_filter_save_get_type ())
+
+typedef struct _CamelMimeFilterSave CamelMimeFilterSave;
+typedef struct _CamelMimeFilterSaveClass CamelMimeFilterSaveClass;
+
+struct _CamelMimeFilterSave {
+ CamelMimeFilter parent;
+
+ struct _CamelMimeFilterSavePrivate *priv;
+
+ char *filename;
+ int fd;
+};
+
+struct _CamelMimeFilterSaveClass {
+ CamelMimeFilterClass parent_class;
+};
+
+guint camel_mime_filter_save_get_type (void);
+CamelMimeFilterSave *camel_mime_filter_save_new (void);
+
+CamelMimeFilterSave *camel_mime_filter_save_new_name (const char *name, int flags, int mode);
+
+#endif /* ! _CAMEL_MIME_FILTER_SAVE_H */
diff --git a/camel/camel-mime-filter.c b/camel/camel-mime-filter.c
new file mode 100644
index 0000000000..de34a4c973
--- /dev/null
+++ b/camel/camel-mime-filter.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "camel-mime-filter.h"
+
+struct _CamelMimeFilterPrivate {
+ char *inbuf;
+ size_t inlen;
+};
+
+#define PRE_HEAD (64)
+#define BACK_HEAD (64)
+#define _PRIVATE(o) (((CamelMimeFilter *)(o))->priv)
+#define FCLASS(o) ((CamelMimeFilterClass *)((GtkObject *)(o))->klass)
+
+static void camel_mime_filter_class_init (CamelMimeFilterClass *klass);
+static void camel_mime_filter_init (CamelMimeFilter *obj);
+
+static GtkObjectClass *camel_mime_filter_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_filter_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeFilter",
+ sizeof (CamelMimeFilter),
+ sizeof (CamelMimeFilterClass),
+ (GtkClassInitFunc) camel_mime_filter_class_init,
+ (GtkObjectInitFunc) camel_mime_filter_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (gtk_object_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ CamelMimeFilter *f = (CamelMimeFilter *)o;
+ struct _CamelMimeFilterPrivate *p = _PRIVATE(f);
+
+ g_free(f->outreal);
+ g_free(f->backbuf);
+ g_free(p->inbuf);
+ g_free(p);
+
+ ((GtkObjectClass *)camel_mime_filter_parent)->finalize (o);
+}
+
+static void
+complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ /* default - do nothing */
+}
+
+static void
+camel_mime_filter_class_init (CamelMimeFilterClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+
+ camel_mime_filter_parent = gtk_type_class (gtk_object_get_type ());
+
+ object_class->finalize = finalise;
+
+ klass->complete = complete;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_filter_init (CamelMimeFilter *obj)
+{
+ obj->outreal = NULL;
+ obj->outbuf = NULL;
+ obj->outsize = 0;
+
+ obj->backbuf = NULL;
+ obj->backsize = 0;
+ obj->backlen = 0;
+
+ _PRIVATE(obj) = g_malloc0(sizeof(*obj->priv));
+}
+
+/**
+ * camel_mime_filter_new:
+ *
+ * Create a new CamelMimeFilter object.
+ *
+ * Return value: A new CamelMimeFilter widget.
+ **/
+CamelMimeFilter *
+camel_mime_filter_new (void)
+{
+ CamelMimeFilter *new = CAMEL_MIME_FILTER ( gtk_type_new (camel_mime_filter_get_type ()));
+ return new;
+}
+
+static void filter_run(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace,
+ void (*filterfunc)(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace))
+{
+ struct _CamelMimeFilterPrivate *p;
+
+ /*
+ here we take a performance hit, if the input buffer doesn't
+ have the pre-space required. We make a buffer that does ...
+ */
+ if (prespace < f->backlen) {
+ int newlen = len+prespace;
+ p = _PRIVATE(f);
+ if (p->inlen < newlen) {
+ /* NOTE: g_realloc copies data, we dont need that (slower) */
+ g_free(p->inbuf);
+ p->inbuf = g_malloc(newlen+PRE_HEAD);
+ p->inlen = newlen+PRE_HEAD;
+ }
+ /* copy to end of structure */
+ memcpy(p->inbuf+p->inlen - len, in, len);
+ in = p->inbuf+p->inlen - len;
+ prespace = p->inlen - len;
+ }
+
+ /* preload any backed up data */
+ if (f->backlen > 0) {
+ memcpy(in-f->backlen, f->backbuf, f->backlen);
+ in -= f->backlen;
+ prespace -= f->backlen;
+ f->backlen = 0;
+ }
+
+ filterfunc(f, in, len, prespace, out, outlen, outprespace);
+}
+
+void camel_mime_filter_filter(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ if (FCLASS(f)->filter)
+ filter_run(f, in, len, prespace, out, outlen, outprespace, FCLASS(f)->filter);
+ else
+ g_error("Filter function unplmenented in class");
+}
+
+void camel_mime_filter_complete(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace)
+{
+ if (FCLASS(f)->complete)
+ filter_run(f, in, len, prespace, out, outlen, outprespace, FCLASS(f)->complete);
+}
+
+void camel_mime_filter_reset(CamelMimeFilter *f)
+{
+ if (FCLASS(f)->reset) {
+ FCLASS(f)->reset(f);
+ }
+
+ /* could free some buffers, if they are really big? */
+ f->backlen = 0;
+}
+
+/* sets number of bytes backed up on the input, new calls replace previous ones */
+void camel_mime_filter_backup(CamelMimeFilter *f, char *data, size_t length)
+{
+ if (f->backsize < length) {
+ /* g_realloc copies data, unnecessary overhead */
+ g_free(f->backbuf);
+ f->backbuf = g_malloc(length+BACK_HEAD);
+ f->backsize = length+BACK_HEAD;
+ }
+ f->backlen = length;
+ memcpy(f->backbuf, data, length);
+}
+
+/* ensure this much size available for filter output (if required) */
+void camel_mime_filter_set_size(CamelMimeFilter *f, size_t size, int keep)
+{
+ if (f->outsize < size) {
+ int offset = f->outptr - f->outreal;
+ if (keep) {
+ f->outreal = g_realloc(f->outreal, size + PRE_HEAD*4);
+ } else {
+ g_free(f->outreal);
+ f->outreal = g_malloc(size + PRE_HEAD*4);
+ }
+ f->outptr = f->outreal + offset;
+ f->outbuf = f->outreal + PRE_HEAD*4;
+ f->outsize = size;
+ /* this could be offset from the end of the structure, but
+ this should be good enough */
+ f->outpre = PRE_HEAD*4;
+ }
+}
+
diff --git a/camel/camel-mime-filter.h b/camel/camel-mime-filter.h
new file mode 100644
index 0000000000..51b63479de
--- /dev/null
+++ b/camel/camel-mime-filter.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Abstract class for non-copying filters */
+
+#ifndef _CAMEL_MIME_FILTER_H
+#define _CAMEL_MIME_FILTER_H
+
+#include <gtk/gtk.h>
+#include <sys/types.h>
+
+#define CAMEL_MIME_FILTER(obj) GTK_CHECK_CAST (obj, camel_mime_filter_get_type (), CamelMimeFilter)
+#define CAMEL_MIME_FILTER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_filter_get_type (), CamelMimeFilterClass)
+#define IS_CAMEL_MIME_FILTER(obj) GTK_CHECK_TYPE (obj, camel_mime_filter_get_type ())
+
+typedef struct _CamelMimeFilter CamelMimeFilter;
+typedef struct _CamelMimeFilterClass CamelMimeFilterClass;
+
+struct _CamelMimeFilter {
+ GtkObject parent;
+
+ struct _CamelMimeFilterPrivate *priv;
+
+ char *outreal; /* real malloc'd buffer */
+ char *outbuf; /* first 'writable' position allowed (outreal + outpre) */
+ char *outptr;
+ int outsize;
+ int outpre; /* prespace of this buffer */
+
+ char *backbuf;
+ int backsize;
+ int backlen; /* significant data there */
+};
+
+struct _CamelMimeFilterClass {
+ GtkObjectClass parent_class;
+
+ /* virtual functions */
+ void (*filter)(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+ void (*complete)(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+ void (*reset)(CamelMimeFilter *f);
+};
+
+guint camel_mime_filter_get_type (void);
+CamelMimeFilter *camel_mime_filter_new (void);
+
+void camel_mime_filter_filter(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+
+void camel_mime_filter_complete(CamelMimeFilter *f,
+ char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+
+void camel_mime_filter_reset(CamelMimeFilter *f);
+
+/* sets/returns number of bytes backed up on the input */
+void camel_mime_filter_backup(CamelMimeFilter *f, char *data, size_t length);
+
+/* ensure this much size available for filter output */
+void camel_mime_filter_set_size(CamelMimeFilter *f, size_t size, int keep);
+
+#endif /* ! _CAMEL_MIME_FILTER_H */
diff --git a/camel/camel-mime-parser.c b/camel/camel-mime-parser.c
new file mode 100644
index 0000000000..cee6454707
--- /dev/null
+++ b/camel/camel-mime-parser.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* What should hopefully be a fast mail parser */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <unicode.h>
+
+#include "camel-mime-parser.h"
+#include "camel-mime-utils.h"
+#include "camel-mime-filter.h"
+#include "camel-stream.h"
+#include "camel-seekable-stream.h"
+
+#define r(x)
+#define h(x)
+#define c(x)
+#define d(x)
+
+#define SCAN_BUF 4096 /* size of read buffer */
+#define SCAN_HEAD 128 /* headroom guaranteed to be before each read buffer */
+
+/* a little hacky, but i couldn't be bothered renaming everything */
+#define _header_scan_state _CamelMimeParserPrivate
+#define _PRIVATE(o) (((CamelMimeParser *)(o))->priv)
+
+struct _header_scan_state {
+
+ /* global state */
+
+ enum _header_state state;
+
+ /* for building headers during scanning */
+ char *outbuf;
+ char *outptr;
+ char *outend;
+
+ int fd; /* input for a fd input */
+ CamelStream *stream; /* or for a stream */
+
+ /* for scanning input buffers */
+ char *realbuf; /* the real buffer, SCAN_HEAD*2 + SCAN_BUF bytes */
+ char *inbuf; /* points to a subset of the allocated memory, the underflow */
+ char *inptr; /* (upto SCAN_HEAD) is for use by filters so they dont copy all data */
+ char *inend;
+
+ int atleast;
+
+ int seek; /* current offset to start of buffer */
+
+ int midline; /* are we mid-line interrupted? */
+ int scan_from; /* do we care about From lines? */
+
+ int start_of_from; /* where from started */
+ int start_of_headers; /* where headers started from the last scan */
+
+ int header_start; /* start of last header, or -1 */
+
+ struct _header_scan_stack *top_part; /* top of message header */
+ int top_start; /* offset of start */
+
+ struct _header_scan_stack *pending; /* if we're pending part info, from the wrong part end */
+
+ /* filters to apply to all content before output */
+ int filterid; /* id of next filter */
+ struct _header_scan_filter *filters;
+
+ /* per message/part info */
+ struct _header_scan_stack *parts;
+
+};
+
+struct _header_scan_stack {
+ struct _header_scan_stack *parent;
+
+ enum _header_state savestate; /* state at invocation of this part */
+
+ struct _header_raw *headers; /* headers for this part */
+
+ struct _header_content_type *content_type;
+
+ char *boundary; /* for multipart/ * boundaries, including leading -- and trailing -- for the final part */
+ int boundarylen; /* length of boundary, including leading -- */
+};
+
+struct _header_scan_filter {
+ struct _header_scan_filter *next;
+ int id;
+ CamelMimeFilter *filter;
+};
+
+static void folder_scan_step(struct _header_scan_state *s, char **databuffer, int *datalength);
+static int folder_scan_init_with_fd(struct _header_scan_state *s, int fd);
+static int folder_scan_init_with_stream(struct _header_scan_state *s, CamelStream *stream);
+static struct _header_scan_state *folder_scan_init(void);
+static void folder_scan_close(struct _header_scan_state *s);
+static struct _header_scan_stack *folder_scan_content(struct _header_scan_state *s, int *lastone, char **data, int *length);
+static struct _header_scan_stack *folder_scan_header(struct _header_scan_state *s, int *lastone);
+static int folder_scan_skip_line(struct _header_scan_state *s);
+static off_t folder_seek(struct _header_scan_state *s, off_t offset, int whence);
+static off_t folder_tell(struct _header_scan_state *s);
+
+static void camel_mime_parser_class_init (CamelMimeParserClass *klass);
+static void camel_mime_parser_init (CamelMimeParser *obj);
+
+static char *states[] = {
+ "HSCAN_INITIAL",
+ "HSCAN_FROM", /* got 'From' line */
+ "HSCAN_HEADER", /* toplevel header */
+ "HSCAN_BODY", /* scanning body of message */
+ "HSCAN_MULTIPART", /* got multipart header */
+ "HSCAN_MESSAGE", /* rfc822 message */
+
+ "HSCAN_PART", /* part of a multipart */
+ "<invalid>",
+
+ "HSCAN_EOF", /* end of file */
+ "HSCAN_FROM_END",
+ "HSCAN_HEAER_END",
+ "HSCAN_BODY_END",
+ "HSCAN_MULTIPART_END",
+ "HSCAN_MESSAGE_END",
+};
+
+static GtkObjectClass *camel_mime_parser_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_mime_parser_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelMimeParser",
+ sizeof (CamelMimeParser),
+ sizeof (CamelMimeParserClass),
+ (GtkClassInitFunc) camel_mime_parser_class_init,
+ (GtkObjectInitFunc) camel_mime_parser_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (gtk_object_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ struct _header_scan_state *s = _PRIVATE(o);
+
+ folder_scan_close(s);
+
+ ((GtkObjectClass *)camel_mime_parser_parent)->finalize (o);
+}
+
+static void
+camel_mime_parser_class_init (CamelMimeParserClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+
+ camel_mime_parser_parent = gtk_type_class (gtk_object_get_type ());
+
+ object_class->finalize = finalise;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_mime_parser_init (CamelMimeParser *obj)
+{
+ struct _header_scan_state *s;
+
+ s = folder_scan_init();
+ _PRIVATE(obj) = s;
+}
+
+/**
+ * camel_mime_parser_new:
+ *
+ * Create a new CamelMimeParser object.
+ *
+ * Return value: A new CamelMimeParser widget.
+ **/
+CamelMimeParser *
+camel_mime_parser_new (void)
+{
+ CamelMimeParser *new = CAMEL_MIME_PARSER ( gtk_type_new (camel_mime_parser_get_type ()));
+ return new;
+}
+
+
+int
+camel_mime_parser_filter_add(CamelMimeParser *m, CamelMimeFilter *mf)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+ struct _header_scan_filter *f, *new;
+
+ new = g_malloc(sizeof(*new));
+ new->filter = mf;
+ new->id = s->filterid++;
+ if (s->filterid == -1)
+ s->filterid++;
+ new->next = 0;
+ gtk_object_ref((GtkObject *)mf);
+
+ /* yes, this is correct, since 'next' is the first element of the struct */
+ f = (struct _header_scan_filter *)&s->filters;
+ while (f->next)
+ f = f->next;
+ f->next = new;
+ return new->id;
+}
+
+void
+camel_mime_parser_filter_remove(CamelMimeParser *m, int id)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+ struct _header_scan_filter *f, *old;
+
+ f = (struct _header_scan_filter *)&s->filters;
+ while (f && f->next) {
+ old = f->next;
+ if (old->id == id) {
+ gtk_object_unref((GtkObject *)old->filter);
+ f->next = old->next;
+ g_free(old);
+ /* there should only be a single matching id, but
+ scan the whole lot anyway */
+ }
+ f = f->next;
+ }
+}
+
+const char *
+camel_mime_parser_header(CamelMimeParser *m, const char *name, int *offset)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ if (s->parts &&
+ s->parts->headers) {
+ return header_raw_find(&s->parts->headers, name, offset);
+ }
+ return NULL;
+}
+
+struct _header_raw *
+camel_mime_parser_headers_raw(CamelMimeParser *m)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ if (s->parts)
+ return s->parts->headers;
+ return NULL;
+}
+
+int
+camel_mime_parser_init_with_fd(CamelMimeParser *m, int fd)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ return folder_scan_init_with_fd(s, fd);
+}
+
+int
+camel_mime_parser_init_with_stream(CamelMimeParser *m, CamelStream *stream)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ return folder_scan_init_with_stream(s, stream);
+}
+
+void
+camel_mime_parser_scan_from(CamelMimeParser *m, int scan_from)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+ s->scan_from = scan_from;
+}
+
+struct _header_content_type *
+camel_mime_parser_content_type(CamelMimeParser *m)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ /* FIXME: should this search up until its found the 'right'
+ content-type? can it? */
+ if (s->parts)
+ return s->parts->content_type;
+ return NULL;
+}
+
+enum _header_state
+camel_mime_parser_step(CamelMimeParser *m, char **databuffer, int *datalength)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ folder_scan_step(s, databuffer, datalength);
+ return s->state;
+}
+
+off_t camel_mime_parser_tell(CamelMimeParser *m)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ return folder_tell(s);
+}
+
+off_t camel_mime_parser_tell_start_headers(CamelMimeParser *m)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ return s->start_of_headers;
+}
+
+off_t camel_mime_parser_tell_start_from(CamelMimeParser *m)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+
+ return s->start_of_from;
+}
+
+off_t camel_mime_parser_seek(CamelMimeParser *m, off_t off, int whence)
+{
+ struct _header_scan_state *s = _PRIVATE(m);
+ return folder_seek(s, off, whence);
+}
+
+/* ********************************************************************** */
+/* Implementation */
+/* ********************************************************************** */
+
+/* read the next bit of data, ensure there is enough room 'atleast' bytes */
+static int
+folder_read(struct _header_scan_state *s)
+{
+ int len;
+ int inoffset;
+
+ if (s->inptr<s->inend-s->atleast)
+ return s->inend-s->inptr;
+
+ /* check for any remaning bytes (under the atleast limit( */
+ inoffset = s->inend - s->inptr;
+ if (inoffset>0) {
+ memcpy(s->inbuf, s->inptr, inoffset);
+ }
+ if (s->stream) {
+ len = camel_stream_read(s->stream, s->inbuf+inoffset, SCAN_BUF-inoffset);
+ } else {
+ len = read(s->fd, s->inbuf+inoffset, SCAN_BUF-inoffset);
+ }
+ r(printf("read %d bytes, offset = %d\n", len, inoffset));
+ if (len>=0) {
+ /* add on the last read block */
+ s->seek += s->inptr - s->inbuf;
+ s->inptr = s->inbuf;
+ s->inend = s->inbuf+len+inoffset;
+ r(printf("content = %d '%.*s'\n",s->inend - s->inptr, s->inend - s->inptr, s->inptr));
+ }
+ r(printf("content = %d '%.*s'\n", s->inend - s->inptr, s->inend - s->inptr, s->inptr));
+ return s->inend-s->inptr;
+}
+
+/* return the current absolute position of the data pointer */
+static off_t
+folder_tell(struct _header_scan_state *s)
+{
+ return s->seek + (s->inptr - s->inbuf);
+}
+
+/*
+ need some way to prime the parser state, so this actually works for
+ other than top-level messages
+*/
+static off_t
+folder_seek(struct _header_scan_state *s, off_t offset, int whence)
+{
+ off_t newoffset;
+ int len;
+
+ if (s->stream) {
+ if (CAMEL_IS_SEEKABLE_STREAM(s->stream)) {
+ /* NOTE: assumes whence seekable stream == whence libc, which is probably
+ the case (or bloody well should've been) */
+ newoffset = camel_seekable_stream_seek((CamelSeekableStream *)s->stream, offset, whence);
+ } else {
+ newoffset = -1;
+ errno = EINVAL;
+ }
+ } else {
+ newoffset = lseek(s->fd, offset, whence);
+ }
+ if (newoffset != -1) {
+ s->seek = newoffset;
+ s->inptr = s->inbuf;
+ s->inend = s->inbuf;
+ if (s->stream)
+ len = camel_stream_read(s->stream, s->inbuf, SCAN_BUF);
+ else
+ len = read(s->fd, s->inbuf, SCAN_BUF);
+ if (len>=0)
+ s->inend = s->inbuf+len;
+ else
+ newoffset = -1;
+ }
+ return newoffset;
+}
+
+static void
+folder_push_part(struct _header_scan_state *s, struct _header_scan_stack *h)
+{
+ h->parent = s->parts;
+ s->parts = h;
+}
+
+static void
+folder_pull_part(struct _header_scan_state *s)
+{
+ struct _header_scan_stack *h;
+
+ h = s->parts;
+ if (h) {
+ s->parts = h->parent;
+ g_free(h->boundary);
+ header_raw_clear(&h->headers);
+ header_content_type_unref(h->content_type);
+ g_free(h);
+ } else {
+ g_warning("Header stack underflow!\n");
+ }
+}
+
+static int
+folder_scan_skip_line(struct _header_scan_state *s)
+{
+ int atleast = s->atleast;
+ register char *inptr, *inend, c;
+ int len;
+
+ s->atleast = 1;
+
+ while ( (len = folder_read(s)) > 0 && len > s->atleast) { /* ensure we have at least enough room here */
+ inptr = s->inptr;
+ inend = s->inend-1;
+
+ c = -1;
+ while (inptr<inend
+ && (c = *inptr++)!='\n')
+ ;
+
+ s->inptr = inptr;
+
+ if (c=='\n') {
+ s->atleast = atleast;
+ return 0;
+ }
+ }
+
+ s->atleast = atleast;
+
+ return -1; /* not found */
+}
+
+static struct _header_scan_stack *
+folder_boundary_check(struct _header_scan_state *s, const char *boundary, int *lastone)
+{
+ struct _header_scan_stack *part;
+ int len = s->atleast-2; /* make sure we dont access past the buffer */
+
+ h(printf("checking boundary marker upto %d bytes\n", len));
+ part = s->parts;
+ while (part) {
+ h(printf(" boundary: %s\n", part->boundary));
+ h(printf(" against: '%.*s'\n", len, boundary));
+ if (part->boundary
+ && part->boundarylen <= len
+ && memcmp(boundary, part->boundary, part->boundarylen)==0) {
+ h(printf("matched boundary: %s\n", part->boundary));
+ /* again, make sure we're in range */
+ if (part->boundarylen <= len+2) {
+ h(printf("checking lastone\n"));
+ *lastone = (boundary[part->boundarylen]=='-'
+ && boundary[part->boundarylen+1]=='-');
+ } else {
+ h(printf("not enough room to check last one?\n"));
+ *lastone = FALSE;
+ }
+ /*printf("ok, we found it! : %s \n", (*lastone)?"Last one":"More to come?");*/
+ return part;
+ }
+ part = part->parent;
+ }
+ return NULL;
+}
+
+static struct _header_scan_stack *
+folder_scan_header(struct _header_scan_state *s, int *lastone)
+{
+ int atleast = s->atleast;
+ register char *inptr, *inend;
+ char *start;
+ int len;
+ struct _header_scan_stack *part, *overpart = s->parts;
+ struct _header_scan_stack *h;
+
+ h(printf("scanning first bit\n"));
+
+ h = g_malloc0(sizeof(*h));
+
+ /* FIXME: this info should be cached ? */
+ part = s->parts;
+ s->atleast = 5;
+ while (part) {
+ if (part->boundary)
+ s->atleast = MAX(s->atleast, part->boundarylen+2);
+ part = part->parent;
+ }
+#if 0
+ s->atleast = MAX(s->atleast, 5);
+ if (s->parts)
+ s->atleast = MAX(s->atleast, s->parts->boundarylen+2);
+#endif
+
+ *lastone = FALSE;
+retry:
+
+ while ((len = folder_read(s))>0 && len >= s->atleast) { /* ensure we have at least enough room here */
+ inptr = s->inptr;
+ inend = s->inend-s->atleast;
+ start = inptr;
+
+ while (inptr<=inend) {
+ register int c=-1;
+ /*printf(" '%.20s'\n", inptr);*/
+
+ if (!s->midline
+ && (part = folder_boundary_check(s, inptr, lastone))) {
+ if ((s->outptr>s->outbuf) || (inptr-start))
+ goto header_truncated; /* may not actually be truncated */
+
+ goto normal_exit;
+ }
+
+ /* goto next line */
+ while (inptr<=inend && (c = *inptr++)!='\n')
+ ;
+
+ /* allocate/append - this wont get executed unless we have *huge* headers,
+ and then probably only once */
+ {
+ register int headerlen = inptr-start;
+ register int len = (s->outend - s->outbuf);
+ char *outnew;
+
+ if (headerlen >= len) {
+ len = (len+headerlen)*2+1;
+ outnew = g_realloc(s->outbuf, len);
+ s->outptr = s->outptr - s->outbuf + outnew;
+ s->outbuf = outnew;
+ s->outend = outnew + len;
+ }
+ memcpy(s->outptr, start, headerlen);
+ s->outptr += headerlen;
+ }
+
+ h(printf("outbuf[0] = %02x '%c' oubuf[1] = %02x '%c'\n",
+ s->outbuf[0], isprint(s->outbuf[0])?s->outbuf[0]:'.',
+ s->outbuf[1], isprint(s->outbuf[1])?s->outbuf[1]:'.'));
+
+ if (s->header_start == -1)
+ s->header_start = (start-s->inbuf) + s->seek;
+
+ if (c!='\n') {
+ s->midline = TRUE;
+ } else {
+ if (!(inptr[0] == ' ' || inptr[0] == '\t')) {
+ if (s->outbuf[0] == '\n'
+ || (s->outbuf[0] == '\r' && s->outbuf[1]=='\n')) {
+ goto header_done;
+ }
+
+ s->outptr[0] = 0;
+
+ d(printf("header %.10s at %d\n", s->outbuf, s->header_start));
+
+ header_raw_append_parse(&h->headers, s->outbuf, s->header_start);
+ if (inptr[0]=='\n'
+ || (inptr[0] == '\r' && inptr[1]=='\n')) {
+ inptr++;
+ goto header_done;
+ }
+ s->outptr = s->outbuf;
+ s->header_start = -1;
+ }
+ s->midline = FALSE;
+ start = inptr;
+ }
+ }
+ s->inptr = inptr;
+ }
+
+ /* ok, we're at the end of the data, just make sure we're not missing out some small
+ truncated header markers */
+ if (overpart) {
+ overpart = overpart->parent;
+ while (overpart) {
+ if (overpart->boundary && (overpart->boundarylen+2) < s->atleast) {
+ s->atleast = overpart->boundarylen+2;
+ h(printf("Retrying next smaller part ...\n"));
+ goto retry;
+ }
+ overpart = overpart->parent;
+ }
+ }
+
+ if ((s->outptr > s->outbuf) || s->inend > s->inptr) {
+ start = s->inptr;
+ inptr = s->inend;
+ goto header_truncated;
+ }
+
+ s->atleast = atleast;
+
+ return h;
+
+header_truncated:
+
+ {
+ register int headerlen = inptr-start;
+ register int len = (s->outend - s->outbuf);
+ char *outnew;
+
+ if (headerlen >= len) {
+ len = (len+headerlen)*2+1;
+ outnew = g_realloc(s->outbuf, len);
+ s->outptr = s->outptr - s->outbuf + outnew;
+ s->outbuf = outnew;
+ s->outend = outnew + len;
+ }
+ memcpy(s->outptr, start, headerlen);
+ s->outptr += headerlen;
+ }
+ s->outptr[0] = 0;
+
+ if (s->header_start == -1)
+ s->header_start = (start-s->inbuf) + s->seek;
+
+ if (s->outbuf[0] == '\n'
+ || (s->outbuf[0] == '\r' && s->outbuf[1]=='\n')) {
+ goto header_done;
+ }
+
+ header_raw_append_parse(&h->headers, s->outbuf, s->header_start);
+
+header_done:
+ part = s->parts;
+
+ s->outptr = s->outbuf;
+normal_exit:
+ s->inptr = inptr;
+ s->atleast = atleast;
+ s->header_start = -1;
+ return h;
+}
+
+static struct _header_scan_stack *
+folder_scan_content(struct _header_scan_state *s, int *lastone, char **data, int *length)
+{
+ int atleast = s->atleast;
+ register char *inptr, *inend;
+ char *start;
+ int len;
+ struct _header_scan_stack *part, *overpart = s->parts;
+ int already_packed = FALSE;
+
+ /*printf("scanning content\n");*/
+
+ /* FIXME: this info should be cached ? */
+ part = s->parts;
+ s->atleast = 5;
+ while (part) {
+ if (part->boundary) {
+ c(printf("boundary: %s\n", part->boundary));
+ s->atleast = MAX(s->atleast, part->boundarylen+2);
+ }
+ part = part->parent;
+ }
+/* s->atleast = MAX(s->atleast, 5);*/
+#if 0
+ if (s->parts)
+ s->atleast = MAX(s->atleast, s->parts->boundarylen+2);
+#endif
+ *lastone = FALSE;
+
+retry:
+ c(printf("atleast = %d\n", s->atleast));
+
+ while ((len = folder_read(s))>0 && len >= s->atleast) { /* ensure we have at least enough room here */
+ inptr = s->inptr;
+ inend = s->inend-s->atleast;
+ start = inptr;
+
+ c(printf("inptr = %p, inend = %p\n", inptr, inend));
+
+ while (inptr<=inend) {
+ if (!s->midline
+ && (part = folder_boundary_check(s, inptr, lastone))) {
+ if ( (inptr-start) )
+ goto content;
+
+ goto normal_exit;
+ }
+ /* goto the next line */
+ while (inptr<=inend && (*inptr++)!='\n')
+ ;
+
+ s->midline = FALSE;
+ }
+
+ /* *sigh* so much for the beautiful simplicity of the code so far - here we
+ have the snot to deal with the nasty end-cases that come from the read-ahead
+ buffers we use */
+ /* what this does, is if we are somewhere near the end of the buffer,
+ force it to the front, and re-read, ensuring we bunch as much together
+ as possible, for the final read, without copying too much of the time */
+ /* make sure we dont loop forever, but also make sure we try smaller
+ boundaries, if there are any, so we dont miss any. */
+ /* this is not needed for the header scanner, since it copies its own
+ data */
+ c(printf("start offset = %d atleast = %d\n", start-s->inbuf, s->atleast));
+ if (start > (s->inbuf + s->atleast)) {
+ /* force a re-scan of this data */
+ s->inptr = start;
+ if (already_packed)
+ goto smaller_boundary;
+ c(printf("near the end, try and bunch things up a bit first\n"));
+ already_packed = TRUE;
+ } else {
+ c(printf("dumping what i've got ...\n"));
+ /* what would be nice here, is if that we're at eof, we bunch the last
+ little bit in the same content, but i dont think this is easy */
+ goto content_mid;
+ }
+ }
+
+ c(printf("length read = %d\n", len));
+smaller_boundary:
+
+ /* ok, we're at the end of the data, just make sure we're not missing out some small
+ truncated header markers */
+ if (overpart) {
+ overpart = overpart->parent;
+ while (overpart) {
+ if (overpart->boundary && (overpart->boundarylen+2) < s->atleast) {
+ s->atleast = overpart->boundarylen+2;
+ c(printf("Retrying next smaller part ...\n"));
+ goto retry;
+ }
+ overpart = overpart->parent;
+ }
+ }
+
+ if (s->inend > s->inptr) {
+ start = s->inptr;
+ inptr = s->inend;
+ goto content;
+ }
+
+ *length = 0;
+ s->atleast = atleast;
+ return NULL;
+
+content_mid:
+ s->midline = TRUE;
+content:
+ part = s->parts;
+normal_exit:
+ s->atleast = atleast;
+ s->inptr = inptr;
+
+ *data = start;
+ *length = inptr-start;
+
+/* printf("got %scontent: %.*s", s->midline?"partial ":"", inptr-start, start);*/
+
+ return part;
+}
+
+
+static void
+folder_scan_close(struct _header_scan_state *s)
+{
+ g_free(s->realbuf);
+ g_free(s->outbuf);
+ while (s->parts)
+ folder_pull_part(s);
+ g_free(s);
+}
+
+
+static struct _header_scan_state *
+folder_scan_init(void)
+{
+ struct _header_scan_state *s;
+
+ s = g_malloc(sizeof(*s));
+
+ s->fd = -1;
+ s->stream = NULL;
+
+ s->outbuf = g_malloc(1024);
+ s->outptr = s->outbuf;
+ s->outend = s->outbuf+1024;
+
+ s->realbuf = g_malloc(SCAN_BUF + SCAN_HEAD*2);
+ s->inbuf = s->realbuf + SCAN_HEAD;
+ s->inptr = s->inbuf;
+ s->inend = s->inbuf;
+ s->atleast = 0;
+
+ s->seek = 0; /* current character position in file of the last read block */
+
+ s->header_start = -1;
+
+ s->start_of_from = -1;
+ s->start_of_headers = -1;
+
+ s->midline = FALSE;
+ s->scan_from = FALSE;
+
+ s->filters = NULL;
+ s->filterid = 1;
+
+ s->parts = NULL;
+
+ s->state = HSCAN_INITIAL;
+ return s;
+}
+
+static int
+folder_scan_init_with_fd(struct _header_scan_state *s, int fd)
+{
+ int len;
+
+ len = read(fd, s->inbuf, SCAN_BUF);
+ if (len>=0) {
+ s->inend = s->inbuf+len;
+ s->fd = fd;
+ if (s->stream) {
+ gtk_object_unref((GtkObject *)s->stream);
+ s->stream = NULL;
+ }
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int
+folder_scan_init_with_stream(struct _header_scan_state *s, CamelStream *stream)
+{
+ int len;
+
+ len = camel_stream_read(stream, s->inbuf, SCAN_BUF);
+ if (len>=0) {
+ s->inend = s->inbuf+len;
+ s->stream = stream;
+ if (s->fd != -1) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+#define USE_FROM
+
+static void
+folder_scan_step(struct _header_scan_state *s, char **databuffer, int *datalength)
+{
+ struct _header_scan_stack *h, *hb;
+ const char *content;
+ const char *bound;
+ int type;
+ int state;
+ struct _header_content_type *ct = NULL;
+ struct _header_scan_filter *f;
+ size_t presize;
+
+/* printf("\nSCAN PASS: state = %d '%s'\n", s->state, states[s->state]);*/
+
+tail_recurse:
+ d({
+ printf("\nSCAN STACK:\n");
+ printf(" '%s' :\n", states[s->state]);
+ hb = s->parts;
+ while (hb) {
+ printf(" '%s' : %s\n", states[hb->savestate], hb->boundary);
+ hb = hb->parent;
+ }
+ printf("\n");
+ });
+
+ switch (s->state) {
+
+ case HSCAN_INITIAL:
+#ifdef USE_FROM
+ if (s->scan_from) {
+ /* FIXME: it would be nice not to have to allocate this every pass */
+ h = g_malloc0(sizeof(*h));
+ h->boundary = g_strdup("From ");
+ h->boundarylen = strlen(h->boundary);
+ folder_push_part(s, h);
+
+ h = s->parts;
+ do {
+ hb = folder_scan_content(s, &state, databuffer, datalength);
+ } while (hb==h && *datalength>0);
+
+ if (*datalength==0 && hb==h) {
+ d(printf("found 'From '\n"));
+ s->start_of_from = folder_tell(s);
+ folder_scan_skip_line(s);
+ h->savestate = HSCAN_INITIAL;
+ s->state = HSCAN_FROM;
+ } else {
+ folder_pull_part(s);
+ s->state = HSCAN_EOF;
+ }
+ return;
+ } else {
+ s->start_of_from = -1;
+ }
+
+#endif
+ case HSCAN_FROM:
+ s->start_of_headers = folder_tell(s);
+ h = folder_scan_header(s, &state);
+#ifdef USE_FROM
+ if (s->scan_from)
+ h->savestate = HSCAN_FROM_END;
+ else
+#endif
+ h->savestate = HSCAN_EOF;
+
+ /* FIXME: should this check for MIME-Version: 1.0 as well? */
+
+ type = HSCAN_HEADER;
+ if ( (content = header_raw_find(&h->headers, "Content-Type", NULL))
+ && (ct = header_content_type_decode(content))) {
+ if (!strcasecmp(ct->type, "multipart")) {
+ bound = header_content_type_param(ct, "boundary");
+ if (bound) {
+ d(printf("multipart, boundary = %s\n", bound));
+ h->boundarylen = strlen(bound)+2;
+ h->boundary = g_malloc(h->boundarylen+3);
+ sprintf(h->boundary, "--%s--", bound);
+ type = HSCAN_MULTIPART;
+ } else {
+ g_warning("Multipart with no boundary, treating as text/plain");
+ }
+ } else if (!strcasecmp(ct->type, "message")) {
+ if (!strcasecmp(ct->subtype, "rfc822")
+ /*|| !strcasecmp(ct->subtype, "partial")*/) {
+ type = HSCAN_MESSAGE;
+ }
+ }
+ }
+ h->content_type = ct;
+ folder_push_part(s, h);
+ s->state = type;
+ return;
+
+ case HSCAN_HEADER:
+ s->state = HSCAN_BODY;
+
+ case HSCAN_BODY:
+ h = s->parts;
+ *datalength = 0;
+ presize = SCAN_HEAD;
+ f = s->filters;
+
+ do {
+ hb = folder_scan_content(s, &state, databuffer, datalength);
+ if (*datalength>0) {
+ d(printf("Content raw: '%.*s'\n", *datalength, *databuffer));
+
+ while (f) {
+ camel_mime_filter_filter(f->filter, *databuffer, *datalength, presize,
+ databuffer, datalength, &presize);
+ f = f->next;
+ }
+ return;
+ }
+ } while (hb==h && *datalength>0);
+
+ /* check for any filter completion data */
+ while (f) {
+ camel_mime_filter_filter(f->filter, *databuffer, *datalength, presize,
+ databuffer, datalength, &presize);
+ f = f->next;
+ }
+ if (*datalength > 0)
+ return;
+
+ s->state = HSCAN_BODY_END;
+ break;
+
+ case HSCAN_MULTIPART:
+ h = s->parts;
+ do {
+ do {
+ hb = folder_scan_content(s, &state, databuffer, datalength);
+ if (*datalength>0) {
+ /* FIXME: needs a state to return this shit??? */
+ d(printf("Multipart Content: '%.*s'\n", *datalength, *databuffer));
+ }
+ } while (hb==h && *datalength>0);
+ if (*datalength==0 && hb==h) {
+ d(printf("got boundary: %s\n", hb->boundary));
+ folder_scan_skip_line(s);
+ if (!state) {
+ s->state = HSCAN_FROM;
+ folder_scan_step(s, databuffer, datalength);
+ s->parts->savestate = HSCAN_MULTIPART; /* set return state for the new head part */
+ return;
+ }
+ } else {
+ break;
+ }
+ } while (1);
+
+ s->state = HSCAN_MULTIPART_END;
+ break;
+
+ case HSCAN_MESSAGE:
+ s->state = HSCAN_FROM;
+ folder_scan_step(s, databuffer, datalength);
+ s->parts->savestate = HSCAN_MESSAGE_END;
+ break;
+
+ case HSCAN_FROM_END:
+ case HSCAN_BODY_END:
+ case HSCAN_MULTIPART_END:
+ case HSCAN_MESSAGE_END:
+ s->state = s->parts->savestate;
+ folder_pull_part(s);
+ if (s->state & HSCAN_END)
+ return;
+ goto tail_recurse;
+
+ case HSCAN_EOF:
+ return;
+
+ default:
+ g_warning("Invalid state in camel-mime-parser: %d", s->state);
+ break;
+ }
+
+ return;
+}
+
+#ifdef STANDALONE
+int main(int argc, char **argv)
+{
+ int fd;
+ struct _header_scan_state *s;
+ char *data;
+ int len;
+ int state;
+ char *name = "/tmp/evmail/Inbox";
+ struct _header_scan_stack *h;
+ int i;
+ int attach = 0;
+
+ if (argc==2)
+ name = argv[1];
+
+ printf("opening: %s", name);
+
+ for (i=1;i<argc;i++) {
+ const char *encoding = NULL, *charset = NULL;
+ char *attachname;
+
+ name = argv[i];
+ printf("opening: %s", name);
+
+ fd = open(name, O_RDONLY);
+ if (fd==-1) {
+ perror("Cannot open mailbox");
+ exit(1);
+ }
+ s = folder_scan_init(fd);
+ s->scan_from = FALSE;
+#if 0
+ h = g_malloc0(sizeof(*h));
+ h->savestate = HSCAN_EOF;
+ folder_push_part(s, h);
+#endif
+ while (s->state != HSCAN_EOF) {
+ folder_scan_step(s, &data, &len);
+ printf("\n -- PARSER STEP RETURN -- %d '%s'\n\n", s->state, states[s->state]);
+ switch (s->state) {
+ case HSCAN_HEADER:
+ if (s->parts->content_type
+ && (charset = header_content_type_param(s->parts->content_type, "charset"))) {
+ if (strcasecmp(charset, "us-ascii")) {
+ folder_push_filter_charset(s, "UTF-8", charset);
+ } else {
+ charset = NULL;
+ }
+ } else {
+ charset = NULL;
+ }
+
+ encoding = header_raw_find(&s->parts->headers, "Content-transfer-encoding");
+ printf("encoding = '%s'\n", encoding);
+ if (encoding && !strncasecmp(encoding, " base64", 7)) {
+ printf("adding base64 filter\n");
+ attachname = g_strdup_printf("attach.%d.%d", i, attach++);
+ folder_push_filter_save(s, attachname);
+ g_free(attachname);
+ folder_push_filter_mime(s, 0);
+ }
+ if (encoding && !strncasecmp(encoding, " quoted-printable", 17)) {
+ printf("adding quoted-printable filter\n");
+ attachname = g_strdup_printf("attach.%d.%d", i, attach++);
+ folder_push_filter_save(s, attachname);
+ g_free(attachname);
+ folder_push_filter_mime(s, 1);
+ }
+
+ break;
+ case HSCAN_BODY:
+ break;
+ case HSCAN_BODY_END:
+ if (encoding && !strncasecmp(encoding, " base64", 7)) {
+ printf("removing filters\n");
+ folder_filter_pull(s);
+ folder_filter_pull(s);
+ }
+ if (encoding && !strncasecmp(encoding, " quoted-printable", 17)) {
+ printf("removing filters\n");
+ folder_filter_pull(s);
+ folder_filter_pull(s);
+ }
+ if (charset) {
+ folder_filter_pull(s);
+ charset = NULL;
+ }
+ encoding = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+ folder_scan_close(s);
+ close(fd);
+ }
+ return 0;
+}
+
+#endif /* STANDALONE */
diff --git a/camel/camel-mime-parser.h b/camel/camel-mime-parser.h
new file mode 100644
index 0000000000..d42ea4709c
--- /dev/null
+++ b/camel/camel-mime-parser.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_PARSER_H
+#define _CAMEL_MIME_PARSER_H
+
+#include <gtk/gtk.h>
+
+#include <camel/camel-mime-utils.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-stream.h>
+
+#define CAMEL_MIME_PARSER(obj) GTK_CHECK_CAST (obj, camel_mime_parser_get_type (), CamelMimeParser)
+#define CAMEL_MIME_PARSER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_mime_parser_get_type (), CamelMimeParserClass)
+#define IS_CAMEL_MIME_PARSER(obj) GTK_CHECK_TYPE (obj, camel_mime_parser_get_type ())
+
+typedef struct _CamelMimeParser CamelMimeParser;
+typedef struct _CamelMimeParserClass CamelMimeParserClass;
+
+/* NOTE: if you add more states, you may need to bump the
+ start of the END tags to 16 or 32, etc - so they are
+ the same as the matching start tag, with a bit difference */
+enum _header_state {
+ HSCAN_INITIAL,
+ HSCAN_FROM, /* got 'From' line */
+ HSCAN_HEADER, /* toplevel header */
+ HSCAN_BODY, /* scanning body of message */
+ HSCAN_MULTIPART, /* got multipart header */
+ HSCAN_MESSAGE, /* rfc822 message */
+
+ HSCAN_PART, /* part of a multipart */
+
+ HSCAN_END = 8, /* bit mask for 'end' flags */
+
+ HSCAN_EOF = 8, /* end of file */
+ HSCAN_FROM_END, /* end of whole from bracket */
+ HSCAN_HEADER_END, /* dummy value */
+ HSCAN_BODY_END, /* end of message */
+ HSCAN_MULTIPART_END, /* end of multipart */
+ HSCAN_MESSAGE_END, /* end of message */
+
+};
+
+struct _CamelMimeParser {
+ GtkObject parent;
+
+ struct _CamelMimeParserPrivate *priv;
+};
+
+struct _CamelMimeParserClass {
+ GtkObjectClass parent_class;
+
+ void (*message)(CamelMimeParser *, void *headers);
+ void (*part)(CamelMimeParser *);
+ void (*content)(CamelMimeParser *);
+};
+
+guint camel_mime_parser_get_type (void);
+CamelMimeParser *camel_mime_parser_new (void);
+
+/* using an fd will be a little faster, but not much (over a simple stream) */
+int camel_mime_parser_init_with_fd(CamelMimeParser *, int fd);
+int camel_mime_parser_init_with_stream(CamelMimeParser *m, CamelStream *stream);
+
+/* scan 'From' separators? */
+void camel_mime_parser_scan_from(CamelMimeParser *, int);
+
+/* normal interface */
+enum _header_state camel_mime_parser_step(CamelMimeParser *, char **, int *);
+
+/* get content type for the current part/header */
+struct _header_content_type *camel_mime_parser_content_type(CamelMimeParser *);
+
+/* get a raw header by name */
+const char *camel_mime_parser_header(CamelMimeParser *, const char *, int *offset);
+/* get all raw headers */
+struct _header_raw *camel_mime_parser_headers_raw(CamelMimeParser *);
+
+/* add a processing filter for body contents */
+int camel_mime_parser_filter_add(CamelMimeParser *, CamelMimeFilter *);
+void camel_mime_parser_filter_remove(CamelMimeParser *, int);
+
+/* these should be used with caution, because the state will not
+ track the seeked position */
+/* FIXME: something to bootstrap the state? */
+off_t camel_mime_parser_tell(CamelMimeParser *);
+off_t camel_mime_parser_seek(CamelMimeParser *, off_t, int);
+
+off_t camel_mime_parser_tell_start_headers(CamelMimeParser *);
+off_t camel_mime_parser_tell_start_from(CamelMimeParser *);
+
+#endif /* ! _CAMEL_MIME_PARSER_H */
diff --git a/camel/camel-mime-part.c b/camel/camel-mime-part.c
index ca4f71dc31..67be67d248 100644
--- a/camel/camel-mime-part.c
+++ b/camel/camel-mime-part.c
@@ -39,9 +39,9 @@
#include "hash-table-utils.h"
#include "camel-stream-mem.h"
#include "camel-mime-part-utils.h"
-#include "gmime-base64.h"
#include "camel-seekable-substream.h"
-#include "camel-stream-b64.h"
+#include "camel-stream-filter.h"
+#include "camel-mime-filter-basic.h"
typedef enum {
@@ -237,13 +237,12 @@ my_finalize (GtkObject *object)
{
CamelMimePart *mime_part = CAMEL_MIME_PART (object);
-#warning do something for (mime_part->disposition) which should not be a GMimeContentField
-
g_free (mime_part->description);
g_free (mime_part->content_id);
g_free (mime_part->content_MD5);
string_list_free (mime_part->content_languages);
g_free (mime_part->filename);
+ header_disposition_unref(mime_part->disposition);
if (mime_part->header_lines) string_list_free (mime_part->header_lines);
if (mime_part->content_type) gmime_content_field_unref (mime_part->content_type);
@@ -313,15 +312,8 @@ camel_mime_part_get_description (CamelMimePart *mime_part)
static void
my_set_disposition (CamelMimePart *mime_part, const gchar *disposition)
{
-#warning Do not use MimeContentfield here !!!
-
- if (mime_part->disposition) {
- g_free (mime_part->disposition->type);
- g_free (mime_part->disposition);
- }
-
- mime_part->disposition = g_new0 (GMimeContentField,1);
- mime_part->disposition->type = g_strdup (disposition);
+ header_disposition_unref(mime_part->disposition);
+ mime_part->disposition = header_disposition_decode(disposition);
}
@@ -340,7 +332,7 @@ static const gchar *
my_get_disposition (CamelMimePart *mime_part)
{
if (!mime_part->disposition) return NULL;
- return (mime_part->disposition)->type;
+ return (mime_part->disposition)->disposition;
}
@@ -605,6 +597,7 @@ my_get_content_object (CamelMedium *medium)
CamelMimePart *mime_part = CAMEL_MIME_PART (medium);
CamelStream *stream;
CamelStream *decoded_stream;
+ CamelMimeFilter *mf = NULL;
if (!medium->content ) {
stream = mime_part->content_input_stream;
@@ -615,16 +608,23 @@ my_get_content_object (CamelMedium *medium)
case CAMEL_MIME_PART_ENCODING_DEFAULT:
case CAMEL_MIME_PART_ENCODING_7BIT:
case CAMEL_MIME_PART_ENCODING_8BIT:
+ break;
+
case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
+ mf = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC);
break;
case CAMEL_MIME_PART_ENCODING_BASE64:
- decoded_stream = camel_stream_b64_new_with_input_stream (stream);
- camel_stream_b64_set_mode (CAMEL_STREAM_B64 (decoded_stream),
- CAMEL_STREAM_B64_DECODER);
+ mf = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
break;
}
+ if (mf) {
+ decoded_stream = (CamelStream *)camel_stream_filter_new_with_stream (stream);
+ camel_stream_filter_add((CamelStreamFilter *)decoded_stream, mf);
+ gtk_object_unref((GtkObject *)mf);
+ }
+
camel_mime_part_construct_content_from_stream (mime_part, decoded_stream);
}
@@ -651,8 +651,8 @@ my_write_content_to_stream (CamelMimePart *mime_part, CamelStream *stream)
{
CamelMedium *medium;
CamelStream *wrapper_stream;
- CamelStreamB64 *stream_b64;
-
+ CamelStream *stream_encode;
+ CamelMimeFilter *mf = NULL;
CamelDataWrapper *content;
g_assert (mime_part);
@@ -672,18 +672,11 @@ my_write_content_to_stream (CamelMimePart *mime_part, CamelStream *stream)
case CAMEL_MIME_PART_ENCODING_8BIT:
camel_data_wrapper_write_to_stream (content, stream);
break;
+ case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
+ mf = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_ENC);
+ break;
case CAMEL_MIME_PART_ENCODING_BASE64:
- /* encode the data wrapper output stream in base 64 ... */
- wrapper_stream = camel_data_wrapper_get_output_stream (content);
- camel_stream_reset (wrapper_stream);
- stream_b64 = CAMEL_STREAM_B64 (camel_stream_b64_new_with_input_stream (wrapper_stream));
- camel_stream_b64_set_mode (stream_b64, CAMEL_STREAM_B64_ENCODER);
-
- /* ... and write it to the output stream in a blocking way */
- camel_stream_b64_write_to_stream (stream_b64, stream);
-
- /* now free the intermediate b64 stream */
- gtk_object_unref (GTK_OBJECT (stream_b64));
+ mf = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
break;
default:
camel_data_wrapper_write_to_stream (content, stream);
@@ -691,6 +684,21 @@ my_write_content_to_stream (CamelMimePart *mime_part, CamelStream *stream)
camel_mime_part_encoding_to_string
(mime_part->encoding));
}
+
+ if (mf) {
+ /* encode the data wrapper output stream in the filtered encoding */
+ wrapper_stream = camel_data_wrapper_get_output_stream (content);
+ camel_stream_reset (wrapper_stream);
+ stream_encode = camel_stream_filter_new_with_stream (wrapper_stream);
+ camel_stream_filter_add((CamelStreamFilter *)stream_encode, mf);
+
+ /* ... and write it to the output stream in a blocking way */
+ camel_stream_write_to_stream (stream_encode, stream);
+
+ /* now free the intermediate b64 stream */
+ gtk_object_unref (GTK_OBJECT (stream_encode));
+ gtk_object_unref((GtkObject *)mf);
+ }
}
@@ -703,7 +711,18 @@ my_write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
CamelMimePart *mp = CAMEL_MIME_PART (data_wrapper);
CamelMedium *medium = CAMEL_MEDIUM (data_wrapper);
- gmime_content_field_write_to_stream(mp->disposition, stream);
+ if (mp->disposition) {
+ struct _header_param *p;
+
+ camel_stream_write_strings(stream, "Content-Disposition: ", mp->disposition->disposition, NULL);
+ /* FIXME: use proper quoting rules here ... */
+ p = mp->disposition->params;
+ while (p) {
+ camel_stream_write_strings (stream, ";\n ", p->name, "= \"", p->value, "\"", NULL);
+ p = p->next;
+ }
+ camel_stream_write_string (stream, "\n");
+ }
WHPT (stream, "Content-Transfer-Encoding",
camel_mime_part_encoding_to_string (mp->encoding));
WHPT (stream, "Content-Description", mp->description);
@@ -848,9 +867,6 @@ my_get_output_stream (CamelDataWrapper *data_wrapper)
return input_stream;
case CAMEL_MIME_PART_ENCODING_BASE64:
- output_stream = camel_stream_b64_new_with_input_stream (input_stream);
- camel_stream_b64_set_mode (CAMEL_STREAM_B64 (output_stream),
- CAMEL_STREAM_B64_ENCODER);
return output_stream;
case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
diff --git a/camel/camel-mime-part.h b/camel/camel-mime-part.h
index a5f956ffe5..37118c8384 100644
--- a/camel/camel-mime-part.h
+++ b/camel/camel-mime-part.h
@@ -37,6 +37,7 @@ extern "C" {
#include <gtk/gtk.h>
#include "camel-types.h"
#include "camel-medium.h"
+#include <camel/camel-mime-utils.h>
#define CAMEL_MIME_PART_TYPE (camel_mime_part_get_type ())
#define CAMEL_MIME_PART(obj) (GTK_CHECK_CAST((obj), CAMEL_MIME_PART_TYPE, CamelMimePart))
@@ -63,7 +64,7 @@ struct _CamelMimePart
/* All fields here are -** PRIVATE **- */
gchar *description;
- GMimeContentField *disposition;
+ CamelMimeDisposition *disposition;
gchar *content_id;
gchar *content_MD5;
GList *content_languages;
diff --git a/camel/camel-mime-utils.c b/camel/camel-mime-utils.c
new file mode 100644
index 0000000000..d3e2406f1a
--- /dev/null
+++ b/camel/camel-mime-utils.c
@@ -0,0 +1,1870 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <iconv.h>
+
+#include <glib.h>
+#include <time.h>
+
+#include <ctype.h>
+
+#include "camel-mime-utils.h"
+
+#define d(x)
+#define d2(x)
+
+static char *base64_alphabet =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static unsigned char tohex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static unsigned char camel_mime_special_table[256] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5,167, 7, 5, 5, 39, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 178,128,140,128,128,128,128,128,140,140,128,128,140,128,136,132,
+ 128,128,128,128,128,128,128,128,128,128,204,140,140, 4,140,132,
+ 140,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
+ 128,128,128,128,128,128,128,128,128,128,128,172,172,172,128,128,
+ 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
+ 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 5,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static unsigned char camel_mime_base64_rank[256] = {
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+};
+
+/*
+ if any of these change, then the tables above should be regenerated
+ by compiling this with -DBUILD_TABLE, and running.
+
+ gcc -o buildtable `glib-config --cflags --libs` -DBUILD_TABLE camel-mime-utils.c
+ ./buildtable
+
+*/
+enum {
+ IS_CTRL = 1<<0,
+ IS_LWSP = 1<<1,
+ IS_TSPECIAL = 1<<2,
+ IS_SPECIAL = 1<<3,
+ IS_SPACE = 1<<4,
+ IS_DSPECIAL = 1<<5,
+ IS_COLON = 1<<6, /* rather wasteful of space ... */
+ IS_QPSAFE = 1<<7
+};
+
+#define is_ctrl(x) ((camel_mime_special_table[(unsigned char)(x)] & IS_CTRL) != 0)
+#define is_lwsp(x) ((camel_mime_special_table[(unsigned char)(x)] & IS_LWSP) != 0)
+#define is_tspecial(x) ((camel_mime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0)
+#define is_type(x, t) ((camel_mime_special_table[(unsigned char)(x)] & (t)) != 0)
+#define is_ttoken(x) ((camel_mime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0)
+#define is_atom(x) ((camel_mime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0)
+#define is_dtext(x) ((camel_mime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0)
+#define is_fieldname(x) ((camel_mime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE|IS_COLON)) == 0)
+#define is_qpsafe(x) ((camel_mime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0)
+
+/* only needs to be run to rebuild the tables above */
+#ifdef BUILD_TABLE
+
+#define CHARS_LWSP " \t\n\r"
+#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?="
+#define CHARS_SPECIAL "()<>@,;:\\\".[]"
+#define CHARS_CSPECIAL "()\\\r" /* not in comments */
+#define CHARS_DSPECIAL "[]\\\r \t" /* not in domains */
+
+static void
+header_init_bits(unsigned char bit, unsigned char bitcopy, int remove, unsigned char *vals, int len)
+{
+ int i;
+
+ if (!remove) {
+ for (i=0;i<len;i++) {
+ camel_mime_special_table[vals[i]] |= bit;
+ }
+ if (bitcopy) {
+ for (i=0;i<256;i++) {
+ if (camel_mime_special_table[i] & bitcopy)
+ camel_mime_special_table[i] |= bit;
+ }
+ }
+ } else {
+ for (i=0;i<256;i++)
+ camel_mime_special_table[i] |= bit;
+ for (i=0;i<len;i++) {
+ camel_mime_special_table[vals[i]] &= ~bit;
+ }
+ if (bitcopy) {
+ for (i=0;i<256;i++) {
+ if (camel_mime_special_table[i] & bitcopy)
+ camel_mime_special_table[i] &= ~bit;
+ }
+ }
+ }
+}
+
+static void
+header_decode_init(void)
+{
+ int i;
+
+ for (i=0;i<256;i++) camel_mime_special_table[i] = 0;
+ for (i=0;i<32;i++) camel_mime_special_table[i] |= IS_CTRL;
+ camel_mime_special_table[127] = IS_CTRL;
+ camel_mime_special_table[' '] = IS_SPACE;
+ camel_mime_special_table[':'] = IS_COLON;
+ header_init_bits(IS_LWSP, 0, 0, CHARS_LWSP, sizeof(CHARS_LWSP)-1);
+ header_init_bits(IS_TSPECIAL, IS_CTRL, 0, CHARS_TSPECIAL, sizeof(CHARS_TSPECIAL)-1);
+ header_init_bits(IS_SPECIAL, 0, 0, CHARS_SPECIAL, sizeof(CHARS_SPECIAL)-1);
+ header_init_bits(IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL, sizeof(CHARS_DSPECIAL)-1);
+ for (i=0;i<256;i++) if ((i>=33 && i<=60) || (i>=62 && i<=126) || i==32 || i==9) camel_mime_special_table[i] |= IS_QPSAFE;
+}
+
+void
+base64_init(void)
+{
+ int i;
+
+ memset(camel_mime_base64_rank, 0xff, sizeof(camel_mime_base64_rank));
+ for (i=0;i<64;i++) {
+ camel_mime_base64_rank[(unsigned int)base64_alphabet[i]] = i;
+ }
+ camel_mime_base64_rank['='] = 0;
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ void run_test(void);
+
+ header_decode_init();
+ base64_init();
+
+ printf("static unsigned char camel_mime_special_table[256] = {\n\t");
+ for (i=0;i<256;i++) {
+ printf("%3d,", camel_mime_special_table[i]);
+ if ((i&15) == 15) {
+ printf("\n");
+ if (i!=255) {
+ printf("\t");
+ }
+ }
+ }
+ printf("};\n");
+
+ printf("static unsigned char camel_mime_base64_rank[256] = {\n\t");
+ for (i=0;i<256;i++) {
+ printf("%3d,", camel_mime_base64_rank[i]);
+ if ((i&15) == 15) {
+ printf("\n");
+ if (i!=255) {
+ printf("\t");
+ }
+ }
+ }
+ printf("};\n");
+
+ run_test();
+
+ return 0;
+}
+
+#endif
+
+
+/* call this when finished encoding everything, to
+ flush off the last little bit */
+int
+base64_encode_close(unsigned char *in, int inlen, unsigned char *out, int *state, int *save)
+{
+ int c1, c2;
+ unsigned char *outptr = out;
+
+ if (inlen>0)
+ outptr += base64_encode_step(in, inlen, outptr, state, save);
+
+ c1 = ((char *)save)[1];
+ c2 = ((char *)save)[2];
+
+ switch (((char *)save)[0]) {
+ case 2:
+ outptr[2] = base64_alphabet [ ( (c2 &0x0f) << 2 ) ];
+ goto skip;
+ case 1:
+ outptr[2] = '=';
+ skip:
+ outptr[0] = base64_alphabet [ c1 >> 2 ];
+ outptr[1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
+ outptr[3] = '=';
+ outptr += 4;
+ break;
+ }
+ *outptr++ = '\n';
+
+ *save = 0;
+ *state = 0;
+
+ return outptr-out;
+}
+
+/*
+ performs an 'encode step', only encodes blocks of 3 characters to the
+ output at a time, saves left-over state in state and save (initialise to
+ 0 on first invocation).
+*/
+int
+base64_encode_step(unsigned char *in, int len, unsigned char *out, int *state, int *save)
+{
+ register unsigned char *inptr, *outptr;
+
+ if (len<=0)
+ return 0;
+
+ inptr = in;
+ outptr = out;
+
+ d(printf("we have %d chars, and %d saved chars\n", len, ((char *)save)[0]));
+
+ if (len + ((char *)save)[0] > 2) {
+ unsigned char *inend = in+len-2;
+ register int c1, c2, c3;
+ register int already;
+
+ already = *state;
+
+ switch (((char *)save)[0]) {
+ case 1: c1 = ((char *)save)[1]; goto skip1;
+ case 2: c1 = ((char *)save)[1];
+ c2 = ((char *)save)[2]; goto skip2;
+ }
+
+ /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
+ while (inptr < inend) {
+ c1 = *inptr++;
+ skip1:
+ c2 = *inptr++;
+ skip2:
+ c3 = *inptr++;
+ *outptr++ = base64_alphabet [ c1 >> 2 ];
+ *outptr++ = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 ) ];
+ *outptr++ = base64_alphabet [ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
+ *outptr++ = base64_alphabet [ c3 & 0x3f ];
+ /* this is a bit ugly ... */
+ if ((++already)>=19) {
+ *outptr++='\n';
+ already = 0;
+ }
+ }
+
+ ((char *)save)[0] = 0;
+ len = 2-(inptr-inend);
+ *state = already;
+ }
+
+ d(printf("state = %d, len = %d\n",
+ (int)((char *)save)[0],
+ len));
+
+ if (len>0) {
+ register char *saveout;
+
+ /* points to the slot for the next char to save */
+ saveout = & (((char *)save)[1]) + ((char *)save)[0];
+
+ /* len can only be 0 1 or 2 */
+ switch(len) {
+ case 2: *saveout++ = *inptr++;
+ case 1: *saveout++ = *inptr++;
+ }
+ ((char *)save)[0]+=len;
+ }
+
+ d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
+ (int)((char *)save)[0],
+ (int)((char *)save)[1],
+ (int)((char *)save)[2]));
+
+ return outptr-out;
+}
+
+int
+base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save)
+{
+ register unsigned char *inptr, *outptr;
+ unsigned char *inend, c;
+ register unsigned int v;
+ int i;
+
+ inend = in+len;
+ outptr = out;
+
+ /* convert 4 base64 bytes to 3 normal bytes */
+ v=*save;
+ i=*state;
+ inptr = in;
+ while (inptr<inend) {
+ c = camel_mime_base64_rank[*inptr++];
+ if (c != 0xff) {
+ v = (v<<6) | c;
+ i++;
+ if (i==4) {
+ *outptr++ = v>>16;
+ *outptr++ = v>>8;
+ *outptr++ = v;
+ i=0;
+ }
+ }
+ }
+
+ *save = v;
+ *state = i;
+
+ /* quick scan back for '=' on the end somewhere */
+ /* fortunately we can drop 1 output char for each trailing = (upto 2) */
+ i=2;
+ while (inptr>in && i) {
+ inptr--;
+ if (camel_mime_base64_rank[*inptr] != 0xff) {
+ if (*inptr == '=')
+ outptr--;
+ i--;
+ }
+ }
+
+ /* if i!= 0 then there is a truncation error! */
+ return outptr-out;
+}
+
+int
+quoted_encode_close(unsigned char *in, int len, unsigned char *out, int *state, int *save)
+{
+ register unsigned char *outptr = out;
+
+ if (len>0)
+ outptr += quoted_encode_step(in, len, outptr, state, save);
+
+ /* hmm, not sure if this should really be added here, we dont want
+ to add it to the content, afterall ...? */
+ *outptr++ = '\n';
+
+ *save = 0;
+ *state = 0;
+
+ return outptr-out;
+}
+
+/*
+ FIXME: does not handle trailing spaces/tabs before end of line
+*/
+int
+quoted_encode_step(unsigned char *in, int len, unsigned char *out, int *state, int *save)
+{
+ register unsigned char *inptr, *outptr, *inend;
+ unsigned char c;
+ register int sofar = *state;
+
+ inptr = in;
+ inend = in+len;
+ outptr = out;
+ while (inptr<inend) {
+ c = *inptr++;
+ if (is_qpsafe(c)) {
+ /* check for soft line-break */
+ if ((++sofar)>74) {
+ *outptr++='=';
+ *outptr++='\n';
+ sofar = 1;
+ }
+ *outptr++=c;
+ } else {
+ if ((++sofar)>72) {
+ *outptr++='=';
+ *outptr++='\n';
+ sofar = 3;
+ }
+ *outptr++ = '=';
+ *outptr++ = tohex[(c>>4) & 0xf];
+ *outptr++ = tohex[c & 0xf];
+ }
+ }
+ *state = sofar;
+ return outptr-out;
+}
+
+/*
+ FIXME: this does not strip trailing spaces from lines (as it should, rfc 2045, section 6.7)
+ Should it also canonicalise the end of line to CR LF??
+
+ Note: Trailing rubbish (at the end of input), like = or =x or =\r will be lost.
+*/
+
+int
+quoted_decode_step(unsigned char *in, int len, unsigned char *out, int *savestate, int *saveme)
+{
+ register unsigned char *inptr, *outptr;
+ unsigned char *inend, c;
+ int state, save;
+
+ inend = in+len;
+ outptr = out;
+
+ d(printf("quoted-printable, decoding text '%.*s'\n", len, in));
+
+ state = *savestate;
+ save = *saveme;
+ inptr = in;
+ while (inptr<inend) {
+ switch (state) {
+ case 0:
+ while (inptr<inend) {
+ c = *inptr++;
+ /* FIXME: use a specials table to avoid 3 comparisons for the common case */
+ if (c=='=') {
+ state = 1;
+ break;
+ }
+#ifdef CANONICALISE_EOL
+ /*else if (c=='\r') {
+ state = 3;
+ } else if (c=='\n') {
+ *outptr++ = '\r';
+ *outptr++ = c;
+ } */
+#endif
+ else {
+ *outptr++ = c;
+ }
+ }
+ break;
+ case 1:
+ c = *inptr++;
+ if (c=='\n') {
+ /* soft break ... unix end of line */
+ state = 0;
+ } else {
+ save = c;
+ state = 2;
+ }
+ break;
+ case 2:
+ c = *inptr++;
+ if (isxdigit(c) && isxdigit(save)) {
+ c = toupper(c);
+ save = toupper(save);
+ *outptr++ = (((save>='A'?save-'A'+10:save-'0')&0x0f) << 4)
+ | ((c>='A'?c-'A'+10:c-'0')&0x0f);
+ } else if (c=='\n' && save == '\r') {
+ /* soft break ... canonical end of line */
+ } else {
+ /* just output the data */
+ *outptr++ = '=';
+ *outptr++ = save;
+ *outptr++ = c;
+ }
+ state = 0;
+ break;
+#ifdef CANONICALISE_EOL
+ case 3:
+ /* convert \r -> to \r\n, leaves \r\n alone */
+ c = *inptr++;
+ if (c=='\n') {
+ *outptr++ = '\r';
+ *outptr++ = c;
+ } else {
+ *outptr++ = '\r';
+ *outptr++ = '\n';
+ *outptr++ = c;
+ }
+ state = 0;
+ break;
+#endif
+ }
+ }
+
+ *savestate = state;
+ *saveme = save;
+
+ return outptr-out;
+}
+
+/*
+ this is for the "Q" encoding of international words,
+ which is slightly different than plain quoted-printable
+*/
+int
+quoted_decode(unsigned char *in, int len, unsigned char *out)
+{
+ register unsigned char *inptr, *outptr;
+ unsigned char *inend, c, c1;
+ int ret = 0;
+
+ inend = in+len;
+ outptr = out;
+
+ d(printf("decoding text '%.*s'\n", len, in));
+
+ inptr = in;
+ while (inptr<inend) {
+ c = *inptr++;
+ if (c=='=') {
+ /* silently ignore truncated data? */
+ if (inend-in>=2) {
+ c = toupper(*inptr++);
+ c1 = toupper(*inptr++);
+ *outptr++ = (((c>='A'?c-'A'+10:c-'0')&0x0f) << 4)
+ | ((c1>='A'?c1-'A'+10:c1-'0')&0x0f);
+ } else {
+ ret = -1;
+ break;
+ }
+ } else if (c=='_') {
+ *outptr++ = 0x20;
+ } else if (c==' ' || c==0x09) {
+ /* FIXME: this is an error! ignore for now ... */
+ ret = -1;
+ break;
+ } else {
+ *outptr++ = c;
+ }
+ }
+ if (ret==0) {
+ return outptr-out;
+ }
+ return -1;
+}
+
+
+static void
+header_decode_lwsp(const char **in)
+{
+ const char *inptr = *in;
+ char c;
+
+ d2(printf("is ws: '%s'\n", *in));
+
+ while (is_lwsp(*inptr) || *inptr =='(') {
+ while (is_lwsp(*inptr)) {
+ d2(printf("(%c)", *inptr));
+ inptr++;
+ }
+ d2(printf("\n"));
+
+ /* check for comments */
+ if (*inptr == '(') {
+ int depth = 1;
+ inptr++;
+ while (depth && (c=*inptr)) {
+ if (c=='\\' && inptr[1]) {
+ inptr++;
+ } else if (c=='(') {
+ depth++;
+ } else if (c==')') {
+ depth--;
+ }
+ inptr++;
+ }
+ }
+ }
+ *in = inptr;
+}
+
+char *
+g_strdup_len(const char *start, int len)
+{
+ char *d = g_malloc(len+1);
+ memcpy(d, start, len);
+ d[len] = 0;
+ return d;
+}
+
+
+/* decode rfc 2047 encoded string segment */
+char *
+rfc2047_decode_word(char *in, int len)
+{
+ char *inptr = in+2;
+ char *inend = in+len-2;
+ char *encname;
+ int tmplen;
+ int ret;
+ char *decword = NULL;
+ char *decoded = NULL;
+ char *outbase = NULL;
+ char *inbuf, *outbuf;
+ int inlen, outlen;
+ iconv_t ic;
+
+ d(printf("decoding '%.*s'\n", len, in));
+
+ /* just make sure we're not passed shit */
+ if (len<7
+ || !(in[0]=='=' && in[1]=='?' && in[len-1]=='=' && in[len-2]=='?')) {
+ d(printf("invalid\n"));
+ return NULL;
+ }
+
+ inptr = memchr(inptr, '?', inend-inptr);
+ if (inptr!=NULL
+ && inptr<inend+2
+ && inptr[2]=='?') {
+ inptr++;
+ tmplen = inend-inptr-2;
+ decword = g_malloc(tmplen); /* this will always be more-than-enough room */
+ switch(toupper(inptr[0])) {
+ case 'Q':
+ inlen = quoted_decode(inptr+2, tmplen, decword);
+ break;
+ case 'B': {
+ int state=0;
+ unsigned int save=0;
+ inlen = base64_decode_step(inptr+2, tmplen, decword, &state, &save);
+ /* if state != 0 then error? */
+ break;
+ }
+ }
+ if (inlen>0) {
+ /* yuck, all this snot is to setup iconv! */
+ tmplen = inptr-in-3;
+ encname = alloca(tmplen+1);
+ encname[tmplen]=0;
+ memcpy(encname, in+2, tmplen);
+
+ inbuf = decword;
+
+ outlen = inlen*6;
+ outbase = g_malloc(outlen);
+ outbuf = outbase;
+
+ ic = iconv_open("utf-8", encname);
+ ret = iconv(ic, (const char **)&inbuf, &inlen, &outbuf, &outlen);
+ iconv_close(ic);
+ if (ret>=0) {
+ *outbuf = 0;
+ decoded = outbase;
+ outbase = NULL;
+ }
+ }
+ }
+ free(outbase);
+ free(decword);
+
+ d(printf("decoded '%s'\n", decoded));
+
+ return decoded;
+}
+
+char *
+decode_coded_string(char *in)
+{
+ return rfc2047_decode_word(in, strlen(in));
+}
+
+/* grrr, glib should have this ! */
+GString *
+g_string_append_len(GString *st, char *s, int l)
+{
+ char *tmp;
+
+ tmp = alloca(l+1);
+ tmp[l]=0;
+ memcpy(tmp, s, l);
+ return g_string_append(st, tmp);
+}
+
+/* decodes a simple text, rfc822 */
+char *
+header_decode_text(char *in, int inlen)
+{
+ GString *out;
+ char *inptr = in;
+ char *inend = in+inlen;
+ char *encstart, *encend;
+ char *decword;
+
+ out = g_string_new("");
+ while ( (encstart = strstr(inptr, "=?"))
+ && (encend = strstr(encstart+2, "?=")) ) {
+
+ decword = rfc2047_decode_word(encstart, encend-encstart+2);
+ if (decword) {
+ g_string_append_len(out, inptr, encstart-inptr);
+ g_string_append_len(out, decword, strlen(decword));
+ free(decword);
+ } else {
+ g_string_append_len(out, inptr, encend-inptr+2);
+ }
+ inptr = encend+2;
+ }
+ g_string_append_len(out, inptr, inend-inptr);
+
+ inptr = out->str;
+ g_string_free(out, FALSE);
+ return inptr;
+}
+
+char *
+header_decode_string(const char *in)
+{
+ if (in == NULL)
+ return NULL;
+ return header_decode_text(in, strlen(in));
+}
+
+
+/* these are all internal parser functions */
+
+char *
+header_decode_token(const char **in)
+{
+ const char *inptr = *in;
+ const char *start;
+
+ header_decode_lwsp(&inptr);
+ start = inptr;
+ while (is_ttoken(*inptr))
+ inptr++;
+ if (inptr>start) {
+ *in = inptr;
+ return g_strdup_len(start, inptr-start);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ <"> * ( <any char except <"> \, cr / \ <any char> ) <">
+*/
+static char *
+header_decode_quoted_string(const char **in)
+{
+ const char *inptr = *in;
+ char *out = NULL, *outptr;
+ int outlen;
+ int c;
+
+ header_decode_lwsp(&inptr);
+ if (*inptr == '"') {
+ const char *intmp;
+ int skip = 0;
+
+ /* first, calc length */
+ inptr++;
+ intmp = inptr;
+ while ( (c = *intmp++) && c!= '"' ) {
+ if (c=='\\' && *intmp) {
+ intmp++;
+ skip++;
+ }
+ }
+ outlen = intmp-inptr-skip;
+ out = outptr = g_malloc(outlen+1);
+ while ( (c = *inptr++) && c!= '"' ) {
+ if (c=='\\' && *inptr) {
+ c = *inptr++;
+ }
+ *outptr++ = c;
+ }
+ *outptr = 0;
+ }
+ *in = inptr;
+ return out;
+}
+
+static char *
+header_decode_atom(const char **in)
+{
+ const char *inptr = *in, *start;
+
+ header_decode_lwsp(&inptr);
+ start = inptr;
+ while (is_atom(*inptr))
+ inptr++;
+ *in = inptr;
+ if (inptr > start)
+ return g_strdup_len(start, inptr-start);
+ else
+ return NULL;
+}
+
+static char *
+header_decode_word(const char **in)
+{
+ const char *inptr = *in;
+
+ header_decode_lwsp(&inptr);
+ if (*inptr == '"') {
+ *in = inptr;
+ return header_decode_quoted_string(in);
+ } else {
+ *in = inptr;
+ return header_decode_atom(in);
+ }
+}
+
+static char *
+header_decode_value(const char **in)
+{
+ const char *inptr = *in;
+
+ header_decode_lwsp(&inptr);
+ if (*inptr == '"') {
+ d(printf("decoding quoted string\n"));
+ return header_decode_quoted_string(in);
+ } else if (is_ttoken(*inptr)) {
+ d(printf("decoding token\n"));
+ /* this may not have the right specials for all params? */
+ return header_decode_token(in);
+ }
+ return NULL;
+}
+
+/* shoudl this return -1 for no int? */
+static int
+header_decode_int(const char **in)
+{
+ const char *inptr = *in;
+ int c, v=0;
+
+ header_decode_lwsp(&inptr);
+ while ( (c=*inptr++ & 0xff)
+ && isdigit(c) ) {
+ v = v*10+(c-'0');
+ }
+ *in = inptr-1;
+ return v;
+}
+
+static int
+header_decode_param(const char **in, char **paramp, char **valuep)
+{
+ const char *inptr = *in;
+ char *param, *value=NULL;
+
+ param = header_decode_token(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == '=') {
+ inptr++;
+ value = header_decode_value(&inptr);
+ }
+
+ if (param && value) {
+ *paramp = param;
+ *valuep = value;
+ *in = inptr;
+ return 0;
+ } else {
+ g_free(param);
+ g_free(value);
+ return 1;
+ }
+}
+
+char *
+header_param(struct _header_param *p, char *name)
+{
+ while (p && strcasecmp(p->name, name) != 0)
+ p = p->next;
+ if (p)
+ return p->value;
+ return NULL;
+}
+
+struct _header_param *
+header_set_param(struct _header_param **l, const char *name, const char *value)
+{
+ struct _header_param *p = (struct _header_param *)l, *pn;
+
+ while (p->next) {
+ pn = p->next;
+ if (!strcasecmp(pn->name, name)) {
+ g_free(pn->value);
+ pn->value = g_strdup(value);
+ return pn;
+ }
+ p = pn;
+ }
+
+ pn = g_malloc(sizeof(*pn));
+ pn->next = 0;
+ pn->name = g_strdup(name);
+ pn->value = g_strdup(value);
+ p->next = pn;
+
+ return pn;
+}
+
+const char *
+header_content_type_param(struct _header_content_type *t, const char *name)
+{
+ if (t==NULL)
+ return NULL;
+ return header_param(t->params, name);
+}
+
+void header_content_type_set_param(struct _header_content_type *t, const char *name, const char *value)
+{
+ header_set_param(&t->params, name, value);
+}
+
+/**
+ * header_content_type_is:
+ * @ct: A content type specifier, or #NULL.
+ * @type: A type to check against.
+ * @subtype: A subtype to check against, or "*" to match any subtype.
+ *
+ * Returns #TRUE if the content type @ct is of type @type/@subtype.
+ * The subtype of "*" will match any subtype. If @ct is #NULL, then
+ * it will match the type "text/plain".
+ *
+ * Return value: #TRUE or #FALSE depending on the matching of the type.
+ **/
+int
+header_content_type_is(struct _header_content_type *ct, const char *type, const char *subtype)
+{
+ /* no type == text/plain or text/"*" */
+ if (ct==NULL) {
+ return (!strcasecmp(type, "text")
+ && (!strcasecmp(subtype, "plain")
+ || !strcasecmp(subtype, "*")));
+ }
+
+ return ((!strcasecmp(ct->type, type)
+ && (!strcasecmp(ct->subtype, subtype)
+ || !strcasecmp("*", subtype))));
+}
+
+void
+header_param_list_free(struct _header_param *p)
+{
+ struct _header_param *n;
+
+ while (p) {
+ n = p->next;
+ g_free(p->name);
+ g_free(p->value);
+ g_free(p);
+ p = n;
+ }
+}
+
+struct _header_content_type *
+header_content_type_new(const char *type, const char *subtype)
+{
+ struct _header_content_type *t = g_malloc(sizeof(*t));
+
+ t->type = type;
+ t->subtype = subtype;
+ t->params = NULL;
+ t->refcount = 1;
+ return t;
+}
+
+void
+header_content_type_ref(struct _header_content_type *ct)
+{
+ if (ct)
+ ct->refcount++;
+}
+
+
+void
+header_content_type_unref(struct _header_content_type *ct)
+{
+ if (ct) {
+ if (ct->refcount <= 1) {
+ header_param_list_free(ct->params);
+ g_free(ct->type);
+ g_free(ct->subtype);
+ g_free(ct);
+ } else {
+ ct->refcount--;
+ }
+ }
+}
+
+/* for decoding email addresses, canonically */
+static char *
+header_decode_domain(const char **in)
+{
+ const char *inptr = *in, *start;
+ int go = TRUE;
+ GString *domain = g_string_new("");
+
+ /* domain ref | domain literal */
+ header_decode_lwsp(&inptr);
+ while (go) {
+ if (*inptr == '[') { /* domain literal */
+ g_string_append(domain, "[ ");
+ inptr++;
+ header_decode_lwsp(&inptr);
+ start = inptr;
+ while (is_dtext(*inptr)) {
+ g_string_append_c(domain, *inptr);
+ inptr++;
+ }
+ if (*inptr == ']') {
+ g_string_append(domain, " ]");
+ inptr++;
+ } else {
+ g_warning("closing ']' not found in domain: %s", *in);
+ }
+ } else {
+ char *a = header_decode_atom(&inptr);
+ if (a) {
+ g_string_append(domain, a);
+ } else {
+ g_warning("missing atom from domain-ref");
+ break;
+ }
+ }
+ header_decode_lwsp(&inptr);
+ if (*inptr == '.') { /* next sub-domain? */
+ g_string_append(domain, " . ");
+ inptr++;
+ header_decode_lwsp(&inptr);
+ } else
+ go = FALSE;
+ }
+
+ *in = inptr;
+
+ /* FIXME:L free string header */
+ return domain->str;
+}
+
+static char *
+header_decode_addrspec(const char **in)
+{
+ const char *inptr = *in;
+ char *word;
+ GString *addr = g_string_new("");
+
+ header_decode_lwsp(&inptr);
+
+ /* addr-spec */
+ word = header_decode_word(&inptr);
+ if (word) {
+ g_string_append(addr, word);
+ header_decode_lwsp(&inptr);
+ while (*inptr == '.' && word) {
+ inptr++;
+ g_string_append_c(addr, '.');
+ word = header_decode_word(&inptr);
+ if (word) {
+ g_string_append(addr, word);
+ header_decode_lwsp(&inptr);
+ } else {
+ g_warning("Invalid address spec: %s", *in);
+ }
+ }
+ if (*inptr == '@') {
+ inptr++;
+ g_string_append_c(addr, '@');
+ word = header_decode_domain(&inptr);
+ if (word) {
+ g_string_append(addr, word);
+ } else {
+ g_warning("Invalid address, missing domain: %s", *in);
+ }
+ } else {
+ g_warning("Invalid addr-spec, missing @: %s", *in);
+ }
+ } else {
+ g_warning("invalid addr-spec, no local part");
+ }
+
+ /* FIXME: return null on error? */
+
+ *in = inptr;
+ word = addr->str;
+ g_string_free(addr, FALSE);
+ return word;
+}
+
+/*
+ address:
+ word *('.' word) @ domain |
+ *(word) '<' [ *('@' domain ) ':' ] word *( '.' word) @ domain |
+
+ 1*word ':' [ word ... etc (mailbox, as above) ] ';'
+ */
+
+/* mailbox:
+ word *( '.' word ) '@' domain
+ *(word) '<' [ *('@' domain ) ':' ] word *( '.' word) @ domain
+ */
+
+/* FIXME: what does this return? */
+
+static void
+header_decode_mailbox(const char **in)
+{
+ const char *inptr = *in;
+ char *pre;
+ int closeme = FALSE;
+ GString *addr;
+
+ addr = g_string_new("");
+
+ /* for each address */
+ pre = header_decode_word(&inptr);
+ header_decode_lwsp(&inptr);
+ if (!(*inptr == '.' || *inptr == '@' || *inptr==',' || *inptr=='\0')) { /* ',' and '\0' required incase it is a simple address, no @ domain part (buggy writer) */
+ /* FIXME: rfc 2047 decode each word */
+ while (pre) {
+ /* rfc_decode(pre) */
+ pre = header_decode_word(&inptr);
+ }
+ header_decode_lwsp(&inptr);
+ if (*inptr == '<') {
+ closeme = TRUE;
+ inptr++;
+ header_decode_lwsp(&inptr);
+ if (*inptr == '@') {
+ while (*inptr == '@') {
+ inptr++;
+ header_decode_domain(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ',') {
+ inptr++;
+ header_decode_lwsp(&inptr);
+ }
+ }
+ if (*inptr == ':') {
+ inptr++;
+ } else {
+ g_warning("broken route-address, missing ':': %s", *in);
+ }
+ }
+ pre = header_decode_word(&inptr);
+ header_decode_lwsp(&inptr);
+ } else {
+ g_warning("broken address? %s", *in);
+ }
+ }
+
+ if (pre) {
+ g_string_append(addr, pre);
+ } else {
+ g_warning("No local-part for email address: %s", *in);
+ }
+
+ /* should be at word '.' localpart */
+ while (*inptr == '.' && pre) {
+ inptr++;
+ pre = header_decode_word(&inptr);
+ g_string_append_c(addr, '.');
+ g_string_append(addr, pre);
+ header_decode_lwsp(&inptr);
+ }
+
+ /* now at '@' domain part */
+ if (*inptr == '@') {
+ char *dom;
+
+ inptr++;
+ g_string_append_c(addr, '@');
+ dom = header_decode_domain(&inptr);
+ g_string_append(addr, dom);
+ } else {
+ g_warning("invalid address, no '@' domain part at %c: %s", *inptr, *in);
+ }
+
+ if (closeme) {
+ header_decode_lwsp(&inptr);
+ if (*inptr == '>') {
+ inptr++;
+ } else {
+ g_warning("invalid route address, no closing '>': %s", *in);
+ }
+ }
+
+ *in = inptr;
+
+ d(printf("got mailbox: %s\n", addr->str));
+}
+
+/* FIXME: what does this return? */
+static void
+header_decode_address(const char **in)
+{
+ const char *inptr = *in;
+ char *pre;
+ GString *group = g_string_new("");
+
+ /* pre-scan, trying to work out format, discard results */
+ header_decode_lwsp(&inptr);
+ while ( (pre = header_decode_word(&inptr)) ) {
+ g_string_append(group, pre);
+ g_string_append(group, " ");
+ g_free(pre);
+ }
+ header_decode_lwsp(&inptr);
+ if (*inptr == ':') {
+ d(printf("group detected: %s\n", group->str));
+ /* that was a group spec, scan mailbox's */
+ inptr++;
+ /* FIXME: check rfc 2047 encodings of words, here or above in the loop */
+ header_decode_lwsp(&inptr);
+ if (*inptr != ';') {
+ int go = TRUE;
+ do {
+ header_decode_mailbox(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ',')
+ inptr++;
+ else
+ go = FALSE;
+ } while (go);
+ if (*inptr == ';') {
+ inptr++;
+ } else {
+ g_warning("Invalid group spec, missing closing ';': %s", *in);
+ }
+ } else {
+ inptr++;
+ }
+ *in = inptr;
+/* } else if (*inptr == '.' || *inptr == '<' || *inptr == '@') {*/
+ } else {
+ /* back-track, and rescan. not worth the code duplication to do this faster */
+ /* this will detect invalid input */
+ header_decode_mailbox(in);
+ }/* else {
+ g_warning("Cannot scan address at '%c': %s", *inptr, *in);
+ }*/
+
+ /* FIXME: store gropu somewhere */
+ g_string_free(group, TRUE);
+}
+
+char *
+header_msgid_decode(const char *in)
+{
+ const char *inptr = in;
+ char *msgid = NULL;
+
+ d(printf("decoding Message-ID: '%s'\n", in));
+
+ if (in == NULL)
+ return NULL;
+
+ header_decode_lwsp(&inptr);
+ if (*inptr == '<') {
+ inptr++;
+ header_decode_lwsp(&inptr);
+ msgid = header_decode_addrspec(&inptr);
+ if (msgid) {
+ header_decode_lwsp(&inptr);
+ if (*inptr == '>') {
+ inptr++;
+ } else {
+ g_warning("Missing closing '>' on message id: %s", in);
+ }
+ } else {
+ g_warning("Cannot find message id in: %s", in);
+ }
+ } else {
+ g_warning("missing opening '<' on message id: %s", in);
+ }
+
+ if (msgid) {
+ d(printf("Got message id: %s\n", msgid));
+ }
+ return msgid;
+}
+
+void
+header_to_decode(const char *in)
+{
+ const char *inptr = in, *last;
+
+ d(printf("decoding To: '%s'\n", in));
+
+ if (in == NULL)
+ return NULL;
+
+ do {
+ last = inptr;
+ header_decode_address(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ',')
+ inptr++;
+ else
+ break;
+ } while (inptr != last);
+
+ if (*inptr) {
+ g_warning("Invalid input detected at %c (%d): %s\n or at: %s", *inptr, inptr-in, in, inptr);
+ }
+
+ if (inptr == last) {
+ g_warning("detected invalid input loop at : %s", last);
+ }
+}
+
+void
+header_mime_decode(const char *in)
+{
+ const char *inptr = in;
+ int major=-1, minor=-1;
+
+ d(printf("decoding MIME-Version: '%s'\n", in));
+
+ if (in == NULL)
+ return NULL;
+
+ header_decode_lwsp(&inptr);
+ if (isdigit(*inptr)) {
+ major = header_decode_int(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == '.') {
+ inptr++;
+ header_decode_lwsp(&inptr);
+ if (isdigit(*inptr))
+ minor = header_decode_int(&inptr);
+ }
+ }
+
+ d(printf("major = %d, minor = %d\n", major, minor));
+}
+
+struct _header_param *
+header_param_list_decode(const char **in)
+{
+ const char *inptr = *in;
+ struct _header_param *head = NULL, *tail = NULL;
+
+ header_decode_lwsp(&inptr);
+ while (*inptr == ';') {
+ char *param, *value;
+ struct _header_param *p;
+
+ inptr++;
+ /* invalid format? */
+ if (header_decode_param(&inptr, &param, &value) != 0)
+ break;
+
+ p = g_malloc(sizeof(*p));
+ p->name = param;
+ p->value = value;
+ p->next = NULL;
+ if (head == NULL)
+ head = p;
+ if (tail)
+ tail->next = p;
+ tail = p;
+ header_decode_lwsp(&inptr);
+ }
+ *in = inptr;
+ return head;
+}
+
+struct _header_content_type *
+header_content_type_decode(const char *in)
+{
+ const char *inptr = in;
+ char *type, *subtype = NULL;
+ struct _header_content_type *t = NULL;
+
+ if (in==NULL)
+ return NULL;
+
+ type = header_decode_token(&inptr);
+ header_decode_lwsp(&inptr);
+ if (type) {
+ if (*inptr == '/') {
+ inptr++;
+ subtype = header_decode_token(&inptr);
+ }
+ if (subtype == NULL && (!strcasecmp(type, "text"))) {
+ g_warning("text type with no subtype, resorting to text/plain: %s", in);
+ subtype = g_strdup("plain");
+ }
+ if (subtype == NULL) {
+ g_warning("MIME type with no subtype: %s", in);
+ }
+
+ t = header_content_type_new(type, subtype);
+ t->params = header_param_list_decode(&inptr);
+ } else {
+ g_free(type);
+ d(printf("cannot find MIME type in header (2) '%s'", in));
+ }
+ return t;
+}
+
+void
+header_content_type_dump(struct _header_content_type *ct)
+{
+ struct _header_param *p;
+
+ printf("Content-Type: ");
+ if (ct==NULL) {
+ printf("<NULL>\n");
+ return;
+ }
+ printf("%s / %s", ct->type, ct->subtype);
+ p = ct->params;
+ if (p) {
+ while (p) {
+ printf(";\n\t%s=\"%s\"", p->name, p->value);
+ p = p->next;
+ }
+ }
+ printf("\n");
+}
+
+char *
+header_content_encoding_decode(const char *in)
+{
+ if (in)
+ return header_decode_token(&in);
+ return NULL;
+}
+
+CamelMimeDisposition *header_disposition_decode(const char *in)
+{
+ CamelMimeDisposition *d = NULL;
+ const char *inptr = in;
+
+ if (in == NULL)
+ return NULL;
+
+ d = g_malloc(sizeof(*d));
+ d->refcount = 1;
+ d->disposition = header_decode_token(&inptr);
+ if (d->disposition == NULL)
+ g_warning("Empty disposition type");
+ d->params = header_param_list_decode(&inptr);
+ return d;
+}
+
+void header_disposition_ref(CamelMimeDisposition *d)
+{
+ if (d)
+ d->refcount++;
+}
+void header_disposition_unref(CamelMimeDisposition *d)
+{
+ if (d) {
+ if (d->refcount<=1) {
+ header_param_list_free(d->params);
+ g_free(d->disposition);
+ g_free(d);
+ } else {
+ d->refcount--;
+ }
+ }
+}
+
+/* hrm, is there a library for this shit? */
+static struct {
+ char *name;
+ int offset;
+} tz_offsets [] = {
+ { "UT", 0 },
+ { "GMT", 0 },
+ { "EST", -500 }, /* these are all US timezones. bloody yanks */
+ { "EDT", -400 },
+ { "CST", -600 },
+ { "CDT", -500 },
+ { "MST", -700 },
+ { "MDT", -600 },
+ { "PST", -800 },
+ { "PDT", -700 },
+ { "Z", 0 },
+ { "A", -100 },
+ { "M", -1200 },
+ { "N", 100 },
+ { "Y", 1200 },
+};
+
+static char *tz_months [] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nove", "Dec"
+};
+
+char *
+header_format_date(time_t time, int offset)
+{
+ struct tm tm;
+
+ d(printf("offset = %d\n", offset));
+
+ d(printf("converting date %s", ctime(&time)));
+
+ time += ((offset / 100) * (60*60)) + (offset % 100)*60;
+
+ d(printf("converting date %s", ctime(&time)));
+
+ memcpy(&tm, gmtime(&time), sizeof(tm));
+
+ return g_strdup_printf("%02d %s %04d %02d:%02d:%02d %c%04d",
+ tm.tm_mday, tz_months[tm.tm_mon],
+ tm.tm_year + 1900,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ offset>=0?'+':'-',
+ offset);
+}
+
+/* convert a date to time_t representation */
+/* this is an awful mess oh well */
+time_t
+header_decode_date(const char *in, int *saveoffset)
+{
+ const char *inptr = in;
+ char *monthname;
+ int year, offset = 0;
+ struct tm tm;
+ int i;
+ time_t t;
+
+ if (in == NULL) {
+ if (*saveoffset)
+ *saveoffset = 0;
+ return 0;
+ }
+
+ d(printf("\ndecoding date '%s'\n", inptr));
+
+ memset(&tm, 0, sizeof(tm));
+
+ header_decode_lwsp(&inptr);
+ if (!isdigit(*inptr)) {
+ char *day = header_decode_token(&inptr);
+ /* we dont really care about the day, its only for display */
+ if (day) {
+ d(printf("got day: %s\n", day));
+ g_free(day);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ',')
+ inptr++;
+ else
+ g_warning("day not followed by ','");
+ }
+ }
+ tm.tm_mday = header_decode_int(&inptr);
+ monthname = header_decode_token(&inptr);
+ if (monthname) {
+ for (i=0;i<sizeof(tz_months)/sizeof(tz_months[0]);i++) {
+ if (!strcasecmp(tz_months[i], monthname)) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ g_free(monthname);
+ }
+ year = header_decode_int(&inptr);
+ if (year<100) {
+ tm.tm_year = year;
+ } else {
+ tm.tm_year = year-1900;
+ }
+ /* get the time ... yurck */
+ tm.tm_hour = header_decode_int(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ':')
+ inptr++;
+ tm.tm_min = header_decode_int(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == ':')
+ inptr++;
+ tm.tm_sec = header_decode_int(&inptr);
+ header_decode_lwsp(&inptr);
+ if (*inptr == '+'
+ || *inptr == '-') {
+ offset = (*inptr++)=='-'?-1:1;
+ offset = offset * header_decode_int(&inptr);
+ d(printf("abs signed offset = %d\n", offset));
+ } else if (isdigit(*inptr)) {
+ offset = header_decode_int(&inptr);
+ d(printf("abs offset = %d\n", offset));
+ } else {
+ char *tz = header_decode_token(&inptr);
+
+ if (tz) {
+ for (i=0;i<sizeof(tz_offsets)/sizeof(tz_offsets[0]);i++) {
+ if (!strcasecmp(tz_offsets[i].name, tz)) {
+ offset = tz_offsets[i].offset;
+ break;
+ }
+ }
+ g_free(tz);
+ }
+ /* some broken mailers seem to put in things like GMT+1030 instead of just +1030 */
+ header_decode_lwsp(&inptr);
+ if (*inptr == '+' || *inptr == '-') {
+ int sign = (*inptr++)=='-'?-1:1;
+ offset = offset + (header_decode_int(&inptr)*sign);
+ }
+ d(printf("named offset = %d\n", offset));
+ }
+
+ /* t -= ( (offset/100) * 60*60) + (offset % 100)*60 + timezone;*/
+
+ t = mktime(&tm) - timezone;
+
+ /* t is now GMT of the time we want, but not offset by the timezone ... */
+
+ d(printf(" gmt normalized? = %s\n", ctime(&t)));
+
+ /* this should convert the time to the GMT equiv time */
+ t -= ( (offset/100) * 60*60) + (offset % 100)*60;
+
+ d(printf(" gmt normalized for timezone? = %s\n", ctime(&t)));
+
+ d({
+ char *tmp;
+ tmp = header_format_date(t, offset);
+ printf(" encoded again: %s\n", tmp);
+ g_free(tmp);
+ });
+
+ if (saveoffset)
+ *saveoffset = offset;
+
+ return t;
+}
+
+/* extra rfc checks */
+#define CHECKS
+
+#ifdef CHECKS
+static void
+check_header(struct _header_raw *h)
+{
+ unsigned char *p;
+
+ p = h->value;
+ while (*p) {
+ if (!isascii(*p)) {
+ g_warning("Appending header violates rfc: %s: %s", h->name, h->value);
+ return;
+ }
+ p++;
+ }
+}
+#endif
+
+void
+header_raw_append_parse(struct _header_raw **list, const char *header, int offset)
+{
+ register const char *in;
+ int fieldlen;
+ char *name;
+
+ in = header;
+ while (is_fieldname(*in))
+ in++;
+ fieldlen = in-header;
+ while (is_lwsp(*in))
+ in++;
+ if (fieldlen == 0 || *in != ':') {
+ printf("Invalid header line: '%s'\n", header);
+ return;
+ }
+ in++;
+ name = alloca(fieldlen+1);
+ memcpy(name, header, fieldlen);
+ name[fieldlen] = 0;
+
+ header_raw_append(list, name, in, offset);
+}
+
+void
+header_raw_append(struct _header_raw **list, const char *name, const char *value, int offset)
+{
+ struct _header_raw *l, *n;
+
+ d(printf("Header: %s: %s\n", name, value));
+
+ n = g_malloc(sizeof(*n));
+ n->next = NULL;
+ n->name = g_strdup(name);
+ n->value = g_strdup(value);
+ n->offset = offset;
+#ifdef CHECKS
+ check_header(n);
+#endif
+ l = (struct _header_raw *)list;
+ while (l->next) {
+ l = l->next;
+ }
+ l->next = n;
+
+ /* debug */
+#if 0
+ if (!strcasecmp(name, "To")) {
+ printf("- Decoding To\n");
+ header_to_decode(value);
+ } else if (!strcasecmp(name, "Content-type")) {
+ printf("- Decoding content-type\n");
+ header_content_type_dump(header_content_type_decode(value));
+ } else if (!strcasecmp(name, "MIME-Version")) {
+ printf("- Decoding mime version\n");
+ header_mime_decode(value);
+ }
+#endif
+}
+
+static struct _header_raw *
+header_raw_find_node(struct _header_raw **list, const char *name)
+{
+ struct _header_raw *l;
+
+ l = *list;
+ while (l) {
+ if (!strcasecmp(l->name, name))
+ break;
+ l = l->next;
+ }
+ return l;
+}
+
+const char *
+header_raw_find(struct _header_raw **list, const char *name, int *offset)
+{
+ struct _header_raw *l;
+
+ l = header_raw_find_node(list, name);
+ if (l) {
+ if (offset)
+ *offset = l->offset;
+ return l->value;
+ } else
+ return NULL;
+}
+
+static void
+header_raw_free(struct _header_raw *l)
+{
+ g_free(l->name);
+ g_free(l->value);
+ g_free(l);
+}
+
+void
+header_raw_remove(struct _header_raw **list, const char *name)
+{
+ struct _header_raw *l, *p;
+
+ /* the next pointer is at the head of the structure, so this is safe */
+ p = (struct _header_raw *)list;
+ l = *list;
+ while (l) {
+ if (!strcasecmp(l->name, name)) {
+ p->next = l->next;
+ header_raw_free(l);
+ l = p->next;
+ } else {
+ p = l;
+ l = l->next;
+ }
+ }
+}
+
+void
+header_raw_replace(struct _header_raw **list, const char *name, const char *value, int offset)
+{
+ header_raw_remove(list, name);
+ header_raw_append(list, name, value, offset);
+}
+
+void
+header_raw_clear(struct _header_raw **list)
+{
+ struct _header_raw *l, *n;
+ l = *list;
+ while (l) {
+ n = l->next;
+ header_raw_free(l);
+ l = n;
+ }
+ *list = NULL;
+}
+
+
+
+#ifdef BUILD_TABLE
+
+/* for debugging tests */
+/* should also have some regression tests somewhere */
+
+void run_test(void)
+{
+ char *to = "gnome hacker dudes: license-discuss@opensource.org,
+ \"Richard M. Stallman\" <rms@gnu.org>,
+ Barry Chester <barry_che@antdiv.gov.au>,
+ Michael Zucchi <zucchi.michael(this (is a nested) comment)@zedzone.mmc.com.au>,
+ Miguel de Icaza <miguel@gnome.org>;,
+ zucchi@zedzone.mmc.com.au, \"Foo bar\" <zed@zedzone>,
+ <frob@frobzone>";
+
+ header_to_decode(to);
+
+ header_mime_decode("1.0");
+ header_mime_decode("1.3 (produced by metasend V1.0)");
+ header_mime_decode("(produced by metasend V1.0) 5.2");
+ header_mime_decode("7(produced by metasend 1.0) . (produced by helix/send/1.0) 9 . 5");
+ header_mime_decode("3.");
+ header_mime_decode(".");
+ header_mime_decode(".5");
+ header_mime_decode("c.d");
+ header_mime_decode("");
+
+ header_msgid_decode(" <\"L3x2i1.0.Nm5.Xd-Wu\"@lists.redhat.com>");
+ header_msgid_decode("<200001180446.PAA02065@beaker.htb.com.au>");
+
+}
+
+#endif /* BUILD_TABLE */
diff --git a/camel/camel-mime-utils.h b/camel/camel-mime-utils.h
new file mode 100644
index 0000000000..931e479a99
--- /dev/null
+++ b/camel/camel-mime-utils.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_MIME_UTILS_H
+#define _CAMEL_MIME_UTILS_H
+
+#include <time.h>
+
+struct _header_param {
+ struct _header_param *next;
+ char *name;
+ char *value;
+};
+
+/* describes a content-type */
+struct _header_content_type {
+ char *type;
+ char *subtype;
+ struct _header_param *params;
+ unsigned int refcount;
+};
+
+/* a raw rfc822 header */
+/* the value MUST be US-ASCII */
+struct _header_raw {
+ struct _header_raw *next;
+ char *name;
+ char *value;
+ int offset; /* in file, if known */
+};
+
+typedef struct _CamelMimeDisposition {
+ char *disposition;
+ struct _header_param *params;
+ unsigned int refcount;
+} CamelMimeDisposition;
+
+struct _header_content_type *header_content_type_new(const char *type, const char *subtype);
+struct _header_content_type *header_content_type_decode(const char *in);
+void header_content_type_unref(struct _header_content_type *ct);
+void header_content_type_ref(struct _header_content_type *ct);
+const char *header_content_type_param(struct _header_content_type *t, const char *name);
+void header_content_type_set_param(struct _header_content_type *t, const char *name, const char *value);
+int header_content_type_is(struct _header_content_type *ct, const char *type, const char *subtype);
+
+char *header_param(struct _header_param *p, char *name);
+struct _header_param *header_set_param(struct _header_param **l, const char *name, const char *value);
+
+CamelMimeDisposition *header_disposition_decode(const char *in);
+void header_disposition_ref(CamelMimeDisposition *);
+void header_disposition_unref(CamelMimeDisposition *);
+
+/* decode the contents of a content-encoding header */
+char *header_content_encoding_decode(const char *in);
+
+/* working with lists of headers */
+void header_raw_append(struct _header_raw **list, const char *name, const char *value, int offset);
+void header_raw_append_parse(struct _header_raw **list, const char *header, int offset);
+const char *header_raw_find(struct _header_raw **list, const char *name, int *ofset);
+void header_raw_replace(struct _header_raw **list, const char *name, const char *value, int offset);
+void header_raw_remove(struct _header_raw **list, const char *name);
+void header_raw_clear(struct _header_raw **list);
+
+/* raw header parsing functions */
+char *header_decode_token(const char **in);
+
+/* decode a string type, like a subject line */
+char *header_decode_string(const char *in);
+
+/* decode an email date field into a GMT time, + optional offset */
+time_t header_decode_date(const char *in, int *saveoffset);
+char *header_format_date(time_t time, int offset);
+
+/* decode a message id */
+char *header_msgid_decode(const char *in);
+
+/* do incremental base64/quoted-printable (de/en)coding */
+int base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save);
+
+int base64_encode_step(unsigned char *in, int len, unsigned char *out, int *state, int *save);
+int base64_encode_close(unsigned char *in, int inlen, unsigned char *out, int *state, int *save);
+
+int quoted_decode_step(unsigned char *in, int len, unsigned char *out, int *savestate, int *saveme);
+
+int quoted_encode_step(unsigned char *in, int len, unsigned char *out, int *state, int *save);
+int quoted_encode_close(unsigned char *in, int len, unsigned char *out, int *state, int *save);
+
+#endif /* ! _CAMEL_MIME_UTILS_H */
diff --git a/camel/camel-stream-b64.c b/camel/camel-stream-b64.c
deleted file mode 100644
index f7f0eeecfd..0000000000
--- a/camel/camel-stream-b64.c
+++ /dev/null
@@ -1,581 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-
-/*
- *
- * Author :
- * Bertrand Guiheneuf <bertrand@helixcode.com>
- *
- * Copyright 1999, 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-stream-b64.h"
-
-#define BSIZE 512
-
-
-
-static CamelStreamClass *parent_class = NULL;
-
-static guchar char_to_six_bits [256] = {
- 128, 128, 128, 128, 128, 128, 128, 128, /* 0 .. 7 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 8 .. 15 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 16 .. 23 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 24 .. 31 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 32 .. 39 */
- 128, 128, 128, 62, 128, 128, 128, 63, /* 40 .. 47 */
- 52, 53, 54, 55, 56, 57, 58, 59, /* 48 .. 55 */
- 60, 61, 128, 128, 128, 64, 128, 128, /* 56 .. 63 */
- 128, 0, 1, 2, 3, 4, 5, 6, /* 64 .. 71 */
- 7, 8, 9, 10, 11, 12, 13, 14, /* 72 .. 79 */
- 15, 16, 17, 18, 19, 20, 21, 22, /* 80 .. 87 */
- 23, 24, 25, 128, 128, 128, 128, 128, /* 88 .. 95 */
- 128, 26, 27, 28, 29, 30, 31, 32, /* 96 .. 103 */
- 33, 34, 35, 36, 37, 38, 39, 40, /* 104 .. 111 */
- 41, 42, 43, 44, 45, 46, 47, 48, /* 112 .. 119 */
- 49, 50, 51, 128, 128, 128, 128, 128, /* 120 .. 127 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 128 .. 135 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 136 .. 143 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 144 .. 151 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 152 .. 159 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 160 .. 167 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 168 .. 175 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 176 .. 183 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 184 .. 191 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 192 .. 199 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 200 .. 207 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 208 .. 215 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 216 .. 223 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 224 .. 231 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 232 .. 239 */
- 128, 128, 128, 128, 128, 128, 128, 128, /* 240 .. 247 */
- 128, 128, 128, 128, 128, 128, 128, 128 /* 248 .. 255 */
-};
-
-
-static gchar six_bits_to_char[65] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-
-/* Returns the class for a CamelStreamB64 */
-#define CSB64_CLASS(so) CAMEL_STREAM_B64_CLASS (GTK_OBJECT(so)->klass)
-
-static void my_init_with_input_stream (CamelStreamB64 *stream_b64,
- CamelStream *input_stream);
-
-static gint my_read (CamelStream *stream,
- gchar *buffer,
- gint n);
-
-static void my_reset (CamelStream *stream);
-
-static gint my_read_decode (CamelStream *stream,
- gchar *buffer,
- gint n);
-static gint my_read_encode (CamelStream *stream,
- gchar *buffer,
- gint n);
-static gboolean my_eos (CamelStream *stream);
-
-static void
-camel_stream_b64_class_init (CamelStreamB64Class *camel_stream_b64_class)
-{
- CamelStreamClass *camel_stream_class = CAMEL_STREAM_CLASS (camel_stream_b64_class);
-
-
- parent_class = gtk_type_class (camel_stream_get_type ());
-
- /* virtual method definition */
- camel_stream_b64_class->init_with_input_stream = my_init_with_input_stream;
-
-
- /* virtual method overload */
- camel_stream_class->read = my_read;
- camel_stream_class->eos = my_eos;
- camel_stream_class->reset = my_reset;
-
- /* signal definition */
-
-}
-
-GtkType
-camel_stream_b64_get_type (void)
-{
- static GtkType camel_stream_b64_type = 0;
-
- if (!camel_stream_b64_type) {
- GtkTypeInfo camel_stream_b64_info =
- {
- "CamelStreamB64",
- sizeof (CamelStreamB64),
- sizeof (CamelStreamB64Class),
- (GtkClassInitFunc) camel_stream_b64_class_init,
- (GtkObjectInitFunc) NULL,
- /* reserved_1 */ NULL,
- /* reserved_2 */ NULL,
- (GtkClassInitFunc) NULL,
- };
-
- camel_stream_b64_type = gtk_type_unique (camel_stream_get_type (), &camel_stream_b64_info);
- }
-
- return camel_stream_b64_type;
-}
-
-
-static void
-my_reemit_available_signal (CamelStream *parent_stream, gpointer user_data)
-{
- gtk_signal_emit_by_name (GTK_OBJECT (user_data), "data_available");
-}
-
-static void
-my_init_with_input_stream (CamelStreamB64 *stream_b64,
- CamelStream *input_stream)
-{
- g_assert (stream_b64);
- g_assert (input_stream);
-
-
-
- /* by default, the stream is in decode mode */
- stream_b64->mode = CAMEL_STREAM_B64_DECODER;
-
- stream_b64->eos = FALSE;
- stream_b64->status.decode_status.keep = 0;
- stream_b64->status.decode_status.state = 0;
-
- stream_b64->input_stream = input_stream;
-
- gtk_object_ref (GTK_OBJECT (input_stream));
-
- /*
- * connect to the parent stream "data_available"
- * stream so that we can reemit the signal on the
- * seekable substream in case some data would
- * be available for us
- */
- gtk_signal_connect (GTK_OBJECT (input_stream),
- "data_available",
- my_reemit_available_signal,
- stream_b64);
-
-
- /* bootstrapping signal */
- gtk_signal_emit_by_name (GTK_OBJECT (stream_b64), "data_available");
-
-
-}
-
-
-
-CamelStream *
-camel_stream_b64_new_with_input_stream (CamelStream *input_stream)
-{
- CamelStreamB64 *stream_b64;
-
- stream_b64 = gtk_type_new (camel_stream_b64_get_type ());
- CSB64_CLASS (stream_b64)->init_with_input_stream (stream_b64, input_stream);
-
- return CAMEL_STREAM (stream_b64);
-}
-
-
-
-
-void
-camel_stream_b64_set_mode (CamelStreamB64 *stream_b64,
- CamelStreamB64Mode mode)
-{
- g_assert (stream_b64);
-
- stream_b64->mode = mode;
-
- if (mode == CAMEL_STREAM_B64_DECODER) {
- stream_b64->status.decode_status.keep = 0;
- stream_b64->status.decode_status.state = 0;
- } else {
- stream_b64->status.encode_status.keep = 0;
- stream_b64->status.encode_status.state = 0;
- stream_b64->status.encode_status.end_state = 0;
- }
-
-}
-
-
-
-
-static gint
-my_read (CamelStream *stream,
- gchar *buffer,
- gint n)
-{
- CamelStreamB64 *stream_b64 = CAMEL_STREAM_B64 (stream);
-
- g_assert (stream);
-
- if (stream_b64->mode == CAMEL_STREAM_B64_DECODER)
- return my_read_decode (stream, buffer, n);
- else
- return my_read_encode (stream, buffer, n);
-}
-
-
-
-static gint
-my_read_decode (CamelStream *stream,
- gchar *buffer,
- gint n)
-{
- CamelStreamB64 *stream_b64 = CAMEL_STREAM_B64 (stream);
- CamelStream64DecodeStatus *status;
- CamelStream *input_stream;
- guchar six_bits_value;
- gint nb_read_in_input;
- guchar c;
- gint j = 0;
-
- g_assert (stream);
- input_stream = stream_b64->input_stream;
-
- g_assert (input_stream);
- status = &(stream_b64->status.decode_status);
-
-
- nb_read_in_input = camel_stream_read (input_stream, &c, 1);
-
- while ((nb_read_in_input >0 ) && (j<n)) {
-
- six_bits_value = char_to_six_bits[c];
-
- /* if we encounter an '=' we can assume the end of the stream
- has been found */
- if (six_bits_value == 64) {
- stream_b64->eos = TRUE;
- status->keep = 0;
-
- break;
- }
-
- /* test if we must ignore the character */
- if (six_bits_value != 128) {
- six_bits_value = six_bits_value & 0x3f;
-
- switch (status->state){
- case 0:
- status->keep = six_bits_value << 2;
-
- break;
- case 1:
- buffer [j++] = status->keep | (six_bits_value >> 4);
- status->keep = (six_bits_value & 0xf) << 4;
- break;
- case 2:
- buffer [j++] = status->keep | (six_bits_value >> 2);
- status->keep = (six_bits_value & 0x3) << 6;
- break;
- case 3:
- buffer [j++] = status->keep | six_bits_value;
- status->keep = 0;
- break;
- }
-
- status->state = (status->state + 1) % 4;
-
- }
-
- if (j<n) nb_read_in_input = camel_stream_read (input_stream, &c, 1);
-
- }
-
- if ((nb_read_in_input == 0) && (camel_stream_eos (input_stream)))
- stream_b64->eos = TRUE;
-
- return j;
-
-}
-
-
-static gint
-my_read_encode (CamelStream *stream,
- gchar *buffer,
- gint n)
-{
- CamelStreamB64 *stream_b64 = CAMEL_STREAM_B64 (stream);
- CamelStream64EncodeStatus *status;
- CamelStream *input_stream;
- gint nb_read_in_input = 0;
- guchar c;
- gint j = 0;
- gboolean end_of_read = FALSE;
-
- g_assert (stream);
- input_stream = stream_b64->input_stream;
-
- g_assert (input_stream);
-
- /* I don't know why the caller would want to
- read a zero length buffer but ... */
- if (n == 0)
- return 0;
-
-
- status = &(stream_b64->status.encode_status);
-
-
- if (status->end_state == 0) {
- /* we are not at the end of the input steam,
- process the data normally */
-
- while ((j<n) && !end_of_read) {
-
- /* check if we must break the encoded line */
- if (status->line_length == 76) {
- buffer [j++] = '\n';
- status->line_length = 0;
- break;
- }
-
- /*
- * because we encode four characters for
- * 3 bytes, the last char does not need any
- * read to write in the stream
- */
- if (status->state == 3) {
- buffer [j++] = six_bits_to_char [status->keep];
- status->state = 0;
- status->keep = 0;
- status->line_length++;
- break;
- }
-
- /*
- * in all the other phases of the stream
- * writing, we need to read a byte from the
- * input stream
- */
- nb_read_in_input = camel_stream_read (input_stream, &c, 1);
-
- if (nb_read_in_input > 0) {
- switch (status->state){
-
- case 0:
- buffer [j++] = six_bits_to_char [(c >> 2) & 0x3f];
- status->keep = (c & 0x3 ) << 4;
- break;
-
- case 1:
- buffer [j++] = six_bits_to_char [status->keep | (c >> 4)];
- status->keep = (c & 0x0f ) << 2;
- break;
-
- case 2:
- buffer [j++] = six_bits_to_char [status->keep | (c >> 6)] ;
- status->keep = (c & 0x3f );
- break;
-
- }
-
- status->state = (status->state + 1) % 4;
- status->line_length++;
- } else
- end_of_read = TRUE;
-
-
- if (camel_stream_eos (input_stream))
- status->end_state = 1;
-
- }
- }
-
- /*
- * now comes the real annoying part. Because some clients
- * expect the b64 encoded sequence length to be multiple of 4,
- * we must pad the end with '='.
- * This is trivial when we write to stream as much as we want
- * but this is not the case when we are limited in the number
- * of chars we can write to the output stream. The consequence
- * of this is that we must keep the state of the writing
- * so that we can resume the next time this routine is called.
- */
-
- if ( status->end_state != 0) {
-
- /*
- * we are at the end of the input stream
- * we must pad the output with '='.
- */
- while ((j<n) && (status->end_state != 6)) {
-
- if (status->end_state == 5) {
-
- status->end_state = 6;
- buffer [j++] = '\n';
- stream_b64->eos = TRUE;
-
- } else {
-
- switch (status->state) {
-
- /*
- * depending on state of the decoder, we need to
- * write different things.
- */
- case 0:
- /*
- * everyting has been written already and the
- * output length is already a multiple of 3
- * so that we have nothing to do.
- */
- status->end_state = 5;
- break;
-
- case 1:
- /*
- * we have something in keep
- * and two '=' we must write
- */
- switch (status->end_state) {
- case 1:
- buffer [j++] = six_bits_to_char [status->keep] ;
- status->end_state++;
- break;
- case 2:
- buffer [j++] = '=';
- status->end_state++;
- break;
- case 3:
- buffer [j++] = '=';
- status->end_state = 5;
- break;
- }
-
-
- break;
-
-
- case 2:
- /*
- * we have something in keep
- * and one '=' we must write
- */
- switch (status->end_state) {
- case 1:
- buffer [j++] = six_bits_to_char [status->keep];
- status->end_state++;
- break;
- case 2:
- buffer [j++] = '=';
- status->end_state = 5;
- break;
- }
-
- break;
-
- case 3:
- /*
- * we have something in keep we must write
- */
- switch (status->end_state) {
- case 1:
- buffer [j++] = six_bits_to_char [status->keep];
- status->end_state++;
- break;
- case 2:
- buffer [j++] = '=';
- status->end_state = 5;
- break;
- }
-
- break;
- }
-
-
- }
- }
-
- }
-
- return j;
-}
-
-
-
-
-
-static gboolean
-my_eos (CamelStream *stream)
-{
- CamelStreamB64 *stream_b64 = CAMEL_STREAM_B64 (stream);
-
- g_assert (stream);
- g_assert (stream_b64->input_stream);
-
- return (stream_b64->eos);
-}
-
-
-
-
-
-static void
-my_reset (CamelStream *stream)
-{
- CamelStreamB64 *stream_b64 = CAMEL_STREAM_B64 (stream);
-
- g_assert (stream);
- g_assert (stream_b64->input_stream);
-
- stream_b64->status.decode_status.keep = 0;
- stream_b64->status.decode_status.state = 0;
-
- stream_b64->eos = FALSE;
-
- camel_stream_reset (stream_b64->input_stream);
-}
-
-
-
-void
-camel_stream_b64_write_to_stream (CamelStreamB64 *stream_b64,
- CamelStream *output_stream)
-{
- gchar tmp_buf[4096];
- gint nb_read;
- gint nb_written;
-
- /*
- * default implementation that uses the input
- * stream and stream it in a blocking way
- * to an output stream.
- */
- g_assert (output_stream);
- g_assert (stream_b64);
-
- while (!camel_stream_eos (CAMEL_STREAM (stream_b64))) {
- nb_read = camel_stream_read (CAMEL_STREAM (stream_b64), tmp_buf, 4096);
- nb_written = 0;
-
- while (nb_written < nb_read)
- nb_written += camel_stream_write (output_stream, tmp_buf + nb_written, nb_read - nb_written);
- }
-
-}
-
-
-
-
diff --git a/camel/camel-stream-b64.h b/camel/camel-stream-b64.h
deleted file mode 100644
index 73bf4f09a4..0000000000
--- a/camel/camel-stream-b64.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-/*
- *
- * Author :
- * Bertrand Guiheneuf <bertrand@helixcode.com>
- *
- * Copyright 1999, 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_STREAM_B64_H
-#define CAMEL_STREAM_B64_H 1
-
-
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus }*/
-
-#include <gtk/gtk.h>
-#include "camel-stream.h"
-
-
-#define CAMEL_STREAM_B64_TYPE (camel_stream_b64_get_type ())
-#define CAMEL_STREAM_B64(obj) (GTK_CHECK_CAST((obj), CAMEL_STREAM_B64_TYPE, CamelStreamB64))
-#define CAMEL_STREAM_B64_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_STREAM_B64_TYPE, CamelStreamB64Class))
-#define CAMEL_IS_STREAM_B64(o) (GTK_CHECK_TYPE((o), CAMEL_STREAM_B64_TYPE))
-
-
-typedef enum {
-
- CAMEL_STREAM_B64_DECODER,
- CAMEL_STREAM_B64_ENCODER
-
-} CamelStreamB64Mode;
-
-
-/* private type */
-typedef struct {
-
- guchar state;
- guchar keep;
-
-} CamelStream64DecodeStatus;
-
-typedef struct {
-
- guchar state;
- guchar keep;
- guchar end_state;
- guchar line_length;
-
-} CamelStream64EncodeStatus;
-
-
-typedef union {
- CamelStream64DecodeStatus decode_status;
- CamelStream64EncodeStatus encode_status;
-} CamelStream64Status;
-
-typedef struct
-{
- CamelStream parent_object;
-
- /* -- all these fields are private -- */
-
- CamelStream *input_stream; /* the stream we get the data from before co/de-coding them */
- CamelStreamB64Mode mode; /* the stream code or decode in B64 depending on that flag */
- gboolean eos;
-
- /* decoding status */
- CamelStream64Status status;
-
-} CamelStreamB64;
-
-
-
-typedef struct {
- CamelStreamClass parent_class;
-
- /* Virtual methods */
- void (*init_with_input_stream) (CamelStreamB64 *stream_b64, CamelStream *input_stream);
-
-} CamelStreamB64Class;
-
-
-
-
-
-
-/* Standard Gtk function */
-GtkType camel_stream_b64_get_type (void);
-
-
-
-
-/* public methods */
-CamelStream * camel_stream_b64_new_with_input_stream (CamelStream *input_stream);
-
-void camel_stream_b64_set_mode (CamelStreamB64 *stream_b64,
- CamelStreamB64Mode mode);
-
-/* utility function that writes the whole de/en-coded
- stream to an output stream */
-void camel_stream_b64_write_to_stream (CamelStreamB64 *stream,
- CamelStream *output_stream);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* CAMEL_STREAM_B64_H */
-
-
-
-
-
-
diff --git a/camel/camel-stream-filter.c b/camel/camel-stream-filter.c
new file mode 100644
index 0000000000..9d6c7d11b0
--- /dev/null
+++ b/camel/camel-stream-filter.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "camel-stream-filter.h"
+
+struct _filter {
+ struct _filter *next;
+ int id;
+ CamelMimeFilter *filter;
+};
+
+struct _CamelStreamFilterPrivate {
+ struct _filter *filters;
+ int filterid; /* next filter id */
+
+ char *realbuffer; /* buffer - READ_PAD */
+ char *buffer; /* READ_SIZE bytes */
+
+ char *filtered; /* the filtered data */
+ size_t filteredlen;
+};
+
+#define READ_PAD (64) /* bytes padded before buffer */
+#define READ_SIZE (4096)
+
+#define _PRIVATE(o) (((CamelStreamFilter *)(o))->priv)
+
+static void camel_stream_filter_class_init (CamelStreamFilterClass *klass);
+static void camel_stream_filter_init (CamelStreamFilter *obj);
+
+static gint do_read (CamelStream *stream, gchar *buffer, gint n);
+static gint do_write (CamelStream *stream, const gchar *buffer, gint n);
+static void do_flush (CamelStream *stream);
+static gboolean do_available (CamelStream *stream);
+static gboolean do_eos (CamelStream *stream);
+static void do_close (CamelStream *stream);
+static void do_reset (CamelStream *stream);
+
+static CamelStreamClass *camel_stream_filter_parent;
+
+enum SIGNALS {
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+guint
+camel_stream_filter_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "CamelStreamFilter",
+ sizeof (CamelStreamFilter),
+ sizeof (CamelStreamFilterClass),
+ (GtkClassInitFunc) camel_stream_filter_class_init,
+ (GtkObjectInitFunc) camel_stream_filter_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ type = gtk_type_unique (camel_stream_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+finalise(GtkObject *o)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)o;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+ struct _filter *fn, *f;
+
+ f = p->filters;
+ while (f) {
+ fn = f->next;
+ gtk_object_unref((GtkObject *)f->filter);
+ g_free(f);
+ f = fn;
+ }
+ g_free(p->realbuffer);
+ g_free(p);
+}
+
+
+static void
+camel_stream_filter_class_init (CamelStreamFilterClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass *) klass;
+ CamelStreamClass *camel_stream_class = (CamelStreamClass *) klass;
+
+ camel_stream_filter_parent = gtk_type_class (camel_stream_get_type ());
+
+ object_class->finalize = finalise;
+
+ camel_stream_class->read = do_read;
+ camel_stream_class->write = do_write;
+ camel_stream_class->flush = do_flush;
+ camel_stream_class->available = do_available;
+ camel_stream_class->eos = do_eos;
+ camel_stream_class->close = do_close;
+ camel_stream_class->reset = do_reset;
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+}
+
+static void
+camel_stream_filter_init (CamelStreamFilter *obj)
+{
+ struct _CamelStreamFilterPrivate *p;
+
+ _PRIVATE(obj) = p = g_malloc0(sizeof(*p));
+ p->realbuffer = g_malloc(READ_SIZE + READ_PAD);
+ p->buffer = p->realbuffer + READ_PAD;
+}
+
+/**
+ * camel_stream_filter_new:
+ *
+ * Create a new CamelStreamFilter object.
+ *
+ * Return value: A new CamelStreamFilter object.
+ **/
+CamelStreamFilter *
+camel_stream_filter_new_with_stream(CamelStream *stream)
+{
+ CamelStreamFilter *new = CAMEL_STREAM_FILTER ( gtk_type_new (camel_stream_filter_get_type ()));
+
+ new->source = stream;
+ return new;
+}
+
+
+/**
+ * camel_stream_filter_add:
+ * @filter: Initialised CamelStreamFilter.
+ * @mf: Filter to perform processing on stream.
+ *
+ * Add a new CamelMimeFilter to execute during the processing of this
+ * stream. Each filter added is processed after the previous one.
+ *
+ * Note that a filter should only be added to a single stream
+ * at a time, otherwise unpredictable results may occur.
+ *
+ * Return value: A filter id for this CamelStreamFilter.
+ **/
+int
+camel_stream_filter_add(CamelStreamFilter *filter, CamelMimeFilter *mf)
+{
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+ struct _filter *fn, *f;
+
+ fn = g_malloc(sizeof(*fn));
+ fn->id = p->filterid++;
+ fn->filter = mf;
+ gtk_object_ref((GtkObject *)mf);
+
+ /* sure, we could use a GList, but we wouldn't save much */
+ f = (struct _filter *)&p->filters;
+ while (f->next)
+ f = f->next;
+ f->next = fn;
+ fn->next = NULL;
+ return fn->id;
+}
+
+/**
+ * camel_stream_filter_remove:
+ * @filter: Initialised CamelStreamFilter.
+ * @id: Filter id, as returned from camel_stream_filter_add().
+ *
+ * Remove a processing filter from the stream, by id.
+ **/
+void
+camel_stream_filter_remove(CamelStreamFilter *filter, int id)
+{
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+ struct _filter *fn, *f;
+
+ f = (struct _filter *)&p->filters;
+ while (f && f->next) {
+ fn = f->next;
+ if (fn->id == id) {
+ f->next = fn->next;
+ gtk_object_unref((GtkObject *)fn->filter);
+ g_free(fn);
+ }
+ f = f->next;
+ }
+}
+
+static gint do_read (CamelStream *stream, gchar *buffer, gint n)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)stream;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+ int size;
+ struct _filter *f;
+
+ if (p->filteredlen<=0) {
+ int presize = READ_SIZE;
+
+ size = camel_stream_read(filter->source, p->buffer, READ_SIZE);
+ if (size<=0) {
+ /* this is somewhat untested */
+ if (camel_stream_eos(filter->source)) {
+ f = p->filters;
+ p->filtered = p->buffer;
+ p->filteredlen = 0;
+ while (f) {
+ camel_mime_filter_complete(f->filter, p->filtered, p->filteredlen, presize, &p->filtered, &p->filteredlen, &presize);
+ f = f->next;
+ }
+ size = p->filteredlen;
+ }
+ if (size<=0)
+ return size;
+ } else {
+ f = p->filters;
+ p->filtered = p->buffer;
+ p->filteredlen = size;
+ while (f) {
+ camel_mime_filter_filter(f->filter, p->filtered, p->filteredlen, presize, &p->filtered, &p->filteredlen, &presize);
+ f = f->next;
+ }
+ }
+ }
+
+ size = MIN(n, p->filteredlen);
+ memcpy(buffer, p->filtered, size);
+ p->filteredlen -= size;
+ p->filtered += size;
+
+ return size;
+}
+
+static gint do_write (CamelStream *stream, const gchar *buffer, gint n)
+{
+ /* what semantics *should* this have?? */
+ g_warning("Writing to a non-writable stream");
+ return -1;
+}
+
+static void do_flush (CamelStream *stream)
+{
+ /* NO OP */
+}
+
+static gboolean do_available (CamelStream *stream)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)stream;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+
+ if (p->filteredlen >0)
+ return TRUE;
+
+ return camel_stream_available(filter->source);
+}
+
+static gboolean do_eos (CamelStream *stream)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)stream;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+
+ if (p->filteredlen >0)
+ return FALSE;
+
+ return camel_stream_eos(filter->source);
+}
+
+static void do_close (CamelStream *stream)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)stream;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+
+ p->filteredlen = 0;
+ camel_stream_close(filter->source);
+}
+
+static void do_reset (CamelStream *stream)
+{
+ CamelStreamFilter *filter = (CamelStreamFilter *)stream;
+ struct _CamelStreamFilterPrivate *p = _PRIVATE(filter);
+ struct _filter *f;
+
+ p->filteredlen = 0;
+ camel_stream_reset(filter->source);
+
+ /* and reset filters */
+ f = p->filters;
+ while (f) {
+ camel_mime_filter_reset(f->filter);
+ f = f->next;
+ }
+}
+
diff --git a/camel/camel-stream-filter.h b/camel/camel-stream-filter.h
new file mode 100644
index 0000000000..b63dae275a
--- /dev/null
+++ b/camel/camel-stream-filter.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CAMEL_STREAM_FILTER_H
+#define _CAMEL_STREAM_FILTER_H
+
+#include <gtk/gtk.h>
+
+#include <camel/camel-stream.h>
+#include <camel/camel-mime-filter.h>
+
+#define CAMEL_STREAM_FILTER(obj) GTK_CHECK_CAST (obj, camel_stream_filter_get_type (), CamelStreamFilter)
+#define CAMEL_STREAM_FILTER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_stream_filter_get_type (), CamelStreamFilterClass)
+#define IS_CAMEL_STREAM_FILTER(obj) GTK_CHECK_TYPE (obj, camel_stream_filter_get_type ())
+
+typedef struct _CamelStreamFilter CamelStreamFilter;
+typedef struct _CamelStreamFilterClass CamelStreamFilterClass;
+
+struct _CamelStreamFilter {
+ CamelStream parent;
+
+ CamelStream *source;
+
+ struct _CamelStreamFilterPrivate *priv;
+};
+
+struct _CamelStreamFilterClass {
+ CamelStreamClass parent_class;
+};
+
+guint camel_stream_filter_get_type (void);
+
+CamelStreamFilter *camel_stream_filter_new_with_stream (CamelStream *stream);
+
+int camel_stream_filter_add (CamelStreamFilter *filter, CamelMimeFilter *);
+void camel_stream_filter_remove (CamelStreamFilter *filter, int id);
+
+#endif /* ! _CAMEL_STREAM_FILTER_H */
diff --git a/camel/camel-stream.c b/camel/camel-stream.c
index 4e7a6c640c..aaa782620e 100644
--- a/camel/camel-stream.c
+++ b/camel/camel-stream.c
@@ -242,3 +242,45 @@ camel_stream_write_strings (CamelStream *stream, ... )
}
va_end (args);
}
+
+/**
+ * camel_stream_write_to_stream:
+ * @stream: Source CamelStream.
+ * @output_stream: Destination CamelStream.
+ *
+ * Write all of a stream (until eos) into another stream, in a blocking
+ * fashion.
+ *
+ * FIXME: This really needs to return an error code.
+ **/
+void
+camel_stream_write_to_stream (CamelStream *stream,
+ CamelStream *output_stream)
+{
+ gchar tmp_buf[4096];
+ gint nb_read;
+ gint nb_written;
+
+ /*
+ * default implementation that uses the input
+ * stream and stream it in a blocking way
+ * to an output stream.
+ */
+ g_assert (output_stream);
+ g_assert (stream);
+
+ while (!camel_stream_eos (CAMEL_STREAM (stream))) {
+ nb_read = camel_stream_read (CAMEL_STREAM (stream), tmp_buf, 4096);
+ if (nb_read>0) {
+ nb_written = 0;
+
+ while (nb_written < nb_read) {
+ int len = camel_stream_write (output_stream, tmp_buf + nb_written, nb_read - nb_written);
+ /* FIXME: what about length 0? */
+ if (len<0)
+ return;
+ nb_written += len;
+ }
+ }
+ }
+}
diff --git a/camel/camel-stream.h b/camel/camel-stream.h
index e7375f41aa..bd1123c1c8 100644
--- a/camel/camel-stream.h
+++ b/camel/camel-stream.h
@@ -94,6 +94,9 @@ void camel_stream_reset (CamelStream *stream);
#define camel_stream_write_string(stream, string) camel_stream_write ((stream), (string), strlen (string))
void camel_stream_write_strings (CamelStream *stream, ... );
+/* write a whole stream to another stream, until eof */
+/* FIXME: this should definetly have an error return code */
+void camel_stream_write_to_stream (CamelStream *stream, CamelStream *output_stream);
#ifdef __cplusplus
}
diff --git a/camel/gmime-base64.c b/camel/gmime-base64.c
deleted file mode 100644
index 3611cc5247..0000000000
--- a/camel/gmime-base64.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Base64 handlers
- *
- * Author:
- * Miguel de Icaza (miguel@kernel.org)
- */
-#include <config.h>
-#include "gmime-base64.h"
-
-#define BSIZE 512
-
-/*
- * 64-based alphabet used by the the Base64 enconding
- */
-static char *base64_alphabet =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/**
- * gmime_encode_base64_to_stream:
- * @input: The data source to be encoded in base64 format
- * @output: Where to put the encoded information in.
- *
- * This routine encodes the information pulled from @input using
- * base64 encoding and stores it on the @output CamelStream object
- */
-void
-gmime_encode_base64_to_stream (CamelStream *input, CamelStream *output)
-{
- char buffer [BSIZE];
- char obuf [80]; /* Output is limited to 76 characters, rfc2045 */
- int n, i, j, state;
- int keep = 0;
-
- state = 0;
- j = 0;
- while ((n = camel_stream_read (input, buffer, sizeof (buffer))) > 0){
- for (i = 0; i < n; i++, state++){
- char c = buffer [i];
-
- switch (state % 3){
- case 0:
- obuf [j++] = base64_alphabet [c >> 2];
- keep = (c & 3) << 4;
- break;
- case 1:
- obuf [j++] = base64_alphabet [keep | (c >> 4)];
- keep = (c & 0xf) << 2;
- break;
- case 2:
- obuf [j++] = base64_alphabet [keep | (c >> 6)];
- obuf [j++] = base64_alphabet [c & 0x3f];
- break;
- }
-
- if (j == 72){
- obuf [j++] = '\r';
- obuf [j++] = '\n';
- camel_stream_write (output, obuf, j);
- j = 0;
- }
- }
- }
-
- switch (state % 3){
- case 0:
- /* full output, nothing left to do */
- break;
-
- case 1:
- obuf [j++] = base64_alphabet [keep];
- obuf [j++] = '=';
- obuf [j++] = '=';
- break;
-
- case 2:
- obuf [j++] = base64_alphabet [keep];
- obuf [j++] = '=';
- break;
- }
- camel_stream_write (output, obuf, j);
- camel_stream_flush (output);
-}
-
-
-
-
-
-
-
-/**
- * gmime_decode_base64:
- * @input: A buffer in base64 format.
- * @output: Destination where the decoded information is stored.
- *
- * This routine decodes the base64 information pulled from @input
- * and stores it on the @output CamelStream object.
- */
-void
-gmime_decode_base64 (CamelStream *input, CamelStream *output)
-{
-}
-
-
-
-
diff --git a/camel/gmime-base64.h b/camel/gmime-base64.h
deleted file mode 100644
index 5dca9c0ff9..0000000000
--- a/camel/gmime-base64.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Base64 handlers
- *
- * Author:
- * Miguel de Icaza (miguel@kernel.org)
- */
-
-#ifndef _GMIME_BASE64_H
-#define _GMIME_BASE64_H
-
-#include "camel-stream.h"
-
-void gmime_encode_base64_to_stream (CamelStream *input, CamelStream *output);
-
-#endif
diff --git a/camel/gmime-content-field.c b/camel/gmime-content-field.c
index 02b44ab7be..6d0abce4e8 100644
--- a/camel/gmime-content-field.c
+++ b/camel/gmime-content-field.c
@@ -28,7 +28,7 @@
#include "gmime-content-field.h"
#include "string-utils.h"
#include <string.h>
-#include "hash-table-utils.h"
+#include "camel-mime-utils.h"
/**
* gmime_content_field_new: Creates a new GMimeContentField object
@@ -49,44 +49,13 @@ gmime_content_field_new (const gchar *type, const gchar *subtype)
GMimeContentField *ctf;
ctf = g_new (GMimeContentField, 1);
- ctf->type = g_strdup (type);
- ctf->subtype = g_strdup (subtype);
- ctf->parameters = g_hash_table_new (g_strcase_hash, g_strcase_equal);
+ ctf->content_type = header_content_type_new(type, subtype);
+ ctf->type = ctf->content_type->type;
+ ctf->subtype = ctf->content_type->subtype;
ctf->ref = 1;
-
return ctf;
}
-
-static void
-_free_parameter (gpointer name, gpointer value, gpointer user_data)
-{
- g_free (name);
- g_free (value);
-}
-
-/**
- * gmime_content_field_free: free a GMimeContentField object
- * @content_field: GMimeContentField object
- *
- * This method destroys the object and should be used very carefully.
- * Use gmime_content_field_unref instead.
- *
- **/
-void
-gmime_content_field_free (GMimeContentField *content_field)
-{
- if (!content_field) return;
-
- g_assert (content_field->ref <= 0);
-
- g_hash_table_foreach (content_field->parameters, _free_parameter, NULL);
- g_free (content_field->type);
- g_free (content_field->subtype);
- g_hash_table_destroy (content_field->parameters);
- g_free (content_field);
-}
-
/**
* gmime_content_field_ref: add a reference to a GMimeContentField object
* @content_field: GMimeContentField object
@@ -100,6 +69,7 @@ void
gmime_content_field_ref (GMimeContentField *content_field)
{
content_field->ref += 1;
+ header_content_type_ref (content_field->content_type);
}
/**
@@ -118,8 +88,9 @@ gmime_content_field_unref (GMimeContentField *content_field)
if (!content_field) return;
content_field->ref -= 1;
+ header_content_type_unref (content_field->content_type);
if (content_field->ref <= 0)
- gmime_content_field_free (content_field);
+ g_free (content_field);
}
@@ -139,44 +110,7 @@ gmime_content_field_unref (GMimeContentField *content_field)
void
gmime_content_field_set_parameter (GMimeContentField *content_field, const gchar *attribute, const gchar *value)
{
- gboolean attribute_exists;
- gchar *old_attribute;
- gchar *old_value;
- attribute_exists = g_hash_table_lookup_extended (content_field->parameters,
- attribute,
- (gpointer *) &old_attribute,
- (gpointer *) &old_value);
- /** CHECK THAT : is normal to free pointers before insertion ? **/
- if (attribute_exists) {
- g_hash_table_remove(content_field->parameters, attribute);
- g_free (old_value);
- g_free (old_attribute);
- }
-
- g_hash_table_insert (content_field->parameters, g_strdup (attribute), g_strdup (value));
-}
-
-
-/**
- * _print_parameter: print a parameter/value pair to a stream as described in RFC 2045
- * @name: name of the parameter
- * @value: value of the parameter
- * @user_data: CamelStream object to write the text to.
- *
- *
- **/
-static void
-_print_parameter (gpointer name, gpointer value, gpointer user_data)
-{
- CamelStream *stream = (CamelStream *)user_data;
-
- camel_stream_write_strings (stream,
- "; \n ",
- (gchar *)name,
- "=",
- (gchar *)value,
- NULL);
-
+ header_content_type_set_param(content_field->content_type, attribute, value);
}
/**
@@ -189,16 +123,21 @@ _print_parameter (gpointer name, gpointer value, gpointer user_data)
void
gmime_content_field_write_to_stream (GMimeContentField *content_field, CamelStream *stream)
{
+ struct _header_param *p;
+
if (!content_field) return;
g_assert (stream);
- if (content_field->type) {
- camel_stream_write_strings (stream, "Content-Type: ", content_field->type, NULL);
- if (content_field->subtype) {
- camel_stream_write_strings (stream, "/", content_field->subtype, NULL);
- }
+ if (content_field->content_type) {
+ camel_stream_write_strings (stream, "Content-Type: ", content_field->content_type->type?content_field->content_type->type:"text",
+ "/", content_field->content_type->subtype?content_field->content_type->subtype:"plain", NULL);
+
/* print all parameters */
- g_hash_table_foreach (content_field->parameters, _print_parameter, stream);
+ p = content_field->content_type->params;
+ while (p) {
+ camel_stream_write_strings (stream, ";\n ", p->name, "= \"", p->value, "\"", NULL);
+ p = p->next;
+ }
camel_stream_write_string (stream, "\n");
}
}
@@ -219,23 +158,15 @@ gmime_content_field_get_mime_type (GMimeContentField *content_field)
{
gchar *mime_type;
- if (!content_field->type) return NULL;
+ if (!content_field->content_type->type) return NULL;
- if (content_field->subtype)
- mime_type = g_strdup_printf ("%s/%s", content_field->type, content_field->subtype);
+ if (content_field->content_type->subtype)
+ mime_type = g_strdup_printf ("%s/%s", content_field->content_type->type, content_field->content_type->subtype);
else
- mime_type = g_strdup (content_field->type);
+ mime_type = g_strdup (content_field->content_type->type);
return mime_type;
}
-static void
-___debug_print_parameter (gpointer name, gpointer value, gpointer user_data)
-{
-
- printf ("****** parameter \"%s\"=\"%s\"\n", (gchar *)name, (gchar *)value);
-
-}
-
/**
* gmime_content_field_get_parameter: return the value of a mime type parameter
* @content_field: content field object
@@ -252,22 +183,10 @@ ___debug_print_parameter (gpointer name, gpointer value, gpointer user_data)
const gchar *
gmime_content_field_get_parameter (GMimeContentField *content_field, const gchar *name)
{
- const gchar *parameter;
- const gchar *old_name;
- gboolean parameter_exists;
+ g_assert (content_field);
- g_assert (content_field->parameters);
g_assert (name);
- /* parameter = (const gchar *)g_hash_table_lookup (content_field->parameters, name); */
- parameter_exists = g_hash_table_lookup_extended (content_field->parameters,
- name,
- (gpointer *) &old_name,
- (gpointer *) &parameter);
- if (!parameter_exists) {
- g_hash_table_foreach (content_field->parameters, ___debug_print_parameter, NULL);
- return NULL;
- }
- return parameter;
+ return header_content_type_param(content_field->content_type, name);
}
@@ -287,76 +206,39 @@ gmime_content_field_get_parameter (GMimeContentField *content_field, const gchar
void
gmime_content_field_construct_from_string (GMimeContentField *content_field, const gchar *string)
{
- gint first, len;
- gint i=0;
- gchar *type, *subtype;
- gchar *param_name, *param_value;
- gboolean param_end;
-
+ struct _header_content_type *new;
+
g_assert (string);
g_assert (content_field);
-
- g_free (content_field->type);
- g_free (content_field->subtype);
-
-
- first = 0;
- len = strlen (string);
- if (!len)
- return;
-
- /* find the type */
- while ( (i<len) && (!strchr ("/;", string[i])) ) i++;
-
- if (i == 0)
- return;
-
- type = g_strndup (string, i);
- string_trim (type, " \t\"", STRING_TRIM_STRIP_TRAILING | STRING_TRIM_STRIP_LEADING);
- content_field->type = type;
- if (i >= len-1) {
- content_field->subtype = NULL;
- return;
- }
-
- first = i+1;
- /* find the subtype, if any */
- if (string[i++] == '/') {
- while ( (i<len) && (string[i] != ';') ) i++;
- if (i != first) {
- subtype = g_strndup (string+first, i-first);
- string_trim (subtype, " \t\"", STRING_TRIM_STRIP_TRAILING | STRING_TRIM_STRIP_LEADING);
- content_field->subtype = subtype;
- if (i >= len-1)
- return;
- }
- }
- first = i+1;
-
- /* parse parameters list */
- param_end = FALSE;
- do {
- while ( (i<len) && (string[i] != '=') ) i++;
- if ((i == len) || (i==first)) param_end = TRUE;
- else {
- /* we have found parameter name */
- param_name = g_strndup (string+first, i-first);
- string_trim (param_name, " \"", STRING_TRIM_STRIP_TRAILING | STRING_TRIM_STRIP_LEADING);
- i++;
- first = i;
- /* Let's find parameter value */
- while ( (i<len) && (string[i] != ';') ) i++;
- if (i != first) param_value = g_strndup (string+first, i-first);
- else param_value = g_strdup ("");
- string_trim (param_value, " \t\"", STRING_TRIM_STRIP_TRAILING | STRING_TRIM_STRIP_LEADING);
- gmime_content_field_set_parameter (content_field, param_name, param_value);
- g_free (param_name);
- g_free (param_value);
- i++;
- first = i;
- }
- } while ((!param_end) && (first < len));
+ new = header_content_type_decode(string);
+ if (content_field->content_type)
+ header_content_type_unref(content_field->content_type);
+ if (new == NULL) {
+ new = header_content_type_new(NULL, NULL);
+ g_warning("Cannot parse content-type string: %s", string);
+ }
+ content_field->content_type = new;
+ content_field->type = new->type;
+ content_field->subtype = new->subtype;
}
+/**
+ * gmime_content_field_is_type:
+ * @content_field: An initialised GMimeContentField.
+ * @type: MIME Major type name.
+ * @subtype: MIME subtype.
+ *
+ * Returns true if the content_field is of the type @type and subtype @subtype.
+ * If @subtype is the special wildcard "*", then it will match any type.
+ *
+ * If the @content_field is empty, then it will match "text/plain", or "text/ *".
+ *
+ * Return value:
+ **/
+int
+gmime_content_field_is_type (GMimeContentField *content_field, const char *type, const char *subtype)
+{
+ return header_content_type_is(content_field->content_type, type, subtype);
+}
diff --git a/camel/gmime-content-field.h b/camel/gmime-content-field.h
index 44d0674bf5..5a28d0fedb 100644
--- a/camel/gmime-content-field.h
+++ b/camel/gmime-content-field.h
@@ -36,13 +36,15 @@ extern "C" {
#include <glib.h>
#include <stdio.h>
-#include "camel-stream.h"
+#include <camel/camel-stream.h>
+#include <camel/camel-mime-utils.h>
typedef struct {
+ struct _header_content_type *content_type;
- gchar *type;
- gchar *subtype;
- GHashTable *parameters;
+ /* these should be deprecated (use the accessors) */
+ char *type; /* these are only copies of the ones in content_type */
+ char *subtype;
gint ref;
@@ -59,6 +61,8 @@ void gmime_content_field_free (GMimeContentField *content_field);
gchar * gmime_content_field_get_mime_type (GMimeContentField *content_field);
const gchar *gmime_content_field_get_parameter (GMimeContentField *content_field, const gchar *name);
+int gmime_content_field_is_type (GMimeContentField *content_field, const char *type, const char *subtype);
+
#ifdef __cplusplus
}
diff --git a/camel/gmime-rfc2047.c b/camel/gmime-rfc2047.c
deleted file mode 100644
index 339a54d521..0000000000
--- a/camel/gmime-rfc2047.c
+++ /dev/null
@@ -1,491 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* gmime-rfc2047.c: implemention of RFC2047 */
-
-/*
- * Author :
- * Bertrand Guiheneuf <bertrand@helixcode.com>
- *
- * Copyright 1999, 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
- *
- */
-
-/*
- * Authors: Robert Brady <rwb197@ecs.soton.ac.uk>
- */
-
-#include <stdio.h>
-#include <ctype.h>
-#include <unicode.h>
-#include <string.h>
-
-#include "gmime-rfc2047.h"
-
-#define NOT_RANKED -1
-
-/* This should be changed ASAP to use the base64 code Miguel comitted */
-
-const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static unsigned char base64_rank[256];
-static int base64_rank_table_built;
-static void build_base64_rank_table (void);
-
-static int
-hexval (gchar c) {
- if (isdigit (c)) return c-'0';
- c = tolower (c);
- return c - 'a' + 10;
-}
-
-static gchar *
-decode_quoted (const gchar *text, const gchar *end)
-{
- gchar *to = malloc(end - text + 1), *to_2 = to;
-
- if (!to) return NULL;
- while (*text && text < end) {
- if (*text == '=') {
- gchar a = hexval (text[1]);
- gchar b = hexval (text[2]);
- int c = (a << 4) + b;
- *to = c;
- to++;
- text+=3;
- } else if (*text == '_') {
- *to = ' ';
- to++;
- text++;
- } else {
- *to = *text;
- to++;
- text++;
- }
- }
- return to_2;
-}
-
-static gchar *
-decode_base64 (const gchar *data, const gchar *end)
-{
- unsigned short pattern = 0;
- int bits = 0;
- int delimiter = '=';
- gchar x;
- gchar *buffer = g_malloc((end - data) * 3);
- gchar *t = buffer;
- int Q = 0;
-
- if (!buffer) return NULL;
-
- while (*data != delimiter) {
- x = base64_rank[(unsigned char)(*data++)];
- if (x == NOT_RANKED)
- continue;
- pattern <<= 6;
- pattern |= x;
- bits += 6;
- if (bits >= 8) {
- x = (pattern >> (bits - 8)) & 0xff;
- *t++ = x;
- Q++;
- bits -= 8;
- }
- }
- *t = 0;
- return buffer;
-}
-
-static void
-build_base64_rank_table (void)
-{
- int i;
-
- if (!base64_rank_table_built) {
- for (i = 0; i < 256; i++)
- base64_rank[i] = NOT_RANKED;
- for (i = 0; i < 64; i++)
- base64_rank[(int) base64_alphabet[i]] = i;
- base64_rank_table_built = 1;
- }
-}
-
-
-static gchar *
-rfc2047_decode_word (const gchar *data, const gchar *into_what)
-{
- const char *charset = strstr (data, "=?"), *encoding, *text, *end;
-
- char *buffer, *b, *cooked_data;
-
- buffer = g_malloc (strlen(data) * 2);
- b = buffer;
-
- if (!charset) return strdup (data);
- charset+=2;
-
- encoding = strchr (charset, '?');
- if (!encoding) return strdup (data);
- encoding++;
-
- text = strchr(encoding, '?');
- if (!text) return strdup (data);
- text++;
-
- end = strstr(text, "?=");
- if (!end) return strdup (data);
-
- b[0] = 0;
-
- if (toupper(*encoding)=='Q')
- cooked_data = decode_quoted (text, end);
- else if (toupper (*encoding)=='B')
- cooked_data = decode_base64 (text, end);
- else
- return g_strdup(data);
-
- {
- char *c = strchr (charset, '?');
- char *q = g_malloc (c - charset + 1);
- char *cook_2 = cooked_data;
- int cook_len = strlen (cook_2);
- int b_len = 4096;
- unicode_iconv_t i;
- strncpy (q, charset, c - charset);
- q[c - charset] = 0;
- i = unicode_iconv_open (into_what, q);
- if (!i) {
- g_free (q);
- return g_strdup (buffer);
- }
- if (unicode_iconv (i, &cook_2, &cook_len, &b, &b_len)==-1)
- /* FIXME : use approximation code if we can't convert it properly. */
- ;
- unicode_iconv_close (i);
- *b = 0;
- }
-
- return g_strdup (buffer);
-}
-
-static const gchar *
-find_end_of_encoded_word (const gchar *data)
-{
- /* We can't just search for ?=,
- because of the case :
- "=?charset?q?=ff?=" :( */
- if (!data) return NULL;
- data = strstr (data, "=?");
- if (!data) return NULL;
- data = strchr(data+2, '?');
- if (!data) return NULL;
- data = strchr (data+1, '?');
- if (!data) return NULL;
- data = strstr (data+1, "?=");
- if (!data) return NULL;
- return data + 2;
-}
-
-gchar *
-gmime_rfc2047_decode (const gchar *data, const gchar *into_what)
-{
- char *buffer = malloc (strlen(data) * 4), *b = buffer;
-
- int was_encoded_word = 0;
-
- build_base64_rank_table ();
-
- while (data && *data) {
- char *word_start = strstr (data, "=?"), *decoded;
- if (!word_start) {
- strcpy (b, data);
- b[strlen (data)] = 0;
- return buffer;
- }
- if (word_start != data) {
-
- if (strspn (data, " \t\n\r") != (word_start - data)) {
- strncpy (b, data, word_start - data);
- b += word_start - data;
- *b = 0;
- }
- }
- decoded = rfc2047_decode_word (word_start, into_what);
- strcpy (b, decoded);
- b += strlen (decoded);
- *b = 0;
- g_free (decoded);
-
- data = find_end_of_encoded_word (data);
- }
-
- *b = 0;
- return buffer;
-}
-
-#define isnt_ascii(a) ((a) <= 0x1f || (a) >= 0x7f)
-
-static int
-rfc2047_clean (const gchar *string, const gchar *max)
-{
- /* if (strstr (string, "?=")) return 1; */
- while (string < max) {
- if (isnt_ascii ((unsigned char)*string))
- return 0;
- string++;
- }
- return 1;
-}
-
-static gchar *
-encode_word (const gchar *string, int length, const gchar *said_charset)
-{
- const gchar *max = string + length;
- if (rfc2047_clean(string, max)) {
- /* don't bother encoding it if it has no odd characters in it */
- return g_strndup (string, length);
- }
- {
- char *temp = malloc (length * 4 + 1), *t = temp;
- t += sprintf (t, "=?%s?q?", said_charset);
- while (string < max) {
- if (*string == ' ')
- *(t++) = '_';
- else if ((*string <= 0x1f) || (*string >= 0x7f) || (*string == '=') || (*string == '?'))
- t += sprintf (t, "=%2x", (unsigned char)*string);
- else
- *(t++) = *string;
-
- string++;
- }
- t += sprintf (t, "?=");
- *t = 0;
- return temp;
- }
-}
-
-static int
-words_in(char *a)
-{
- int words = 1;
- while (*a) {
- if (*(a++)==' ')
- words++;
- }
- return words;
-}
-
-struct word_data {
- const char *word;
- int word_length;
- const char *to_encode_in;
- char *encoded;
- enum {
- wt_None,
- wt_Address,
- } type;
-};
-
-static int string_can_fit_in(const char *a, int length, const char *charset)
-{
- while (length--) {
- if (*a < 0x1f || *a >= 0x7f) return 0;
- a++;
- }
- return 1;
-}
-
-static void
-show_entry(struct word_data *a)
-{
- a->type = wt_None;
-
- if (string_can_fit_in(a->word, a->word_length, "US-ASCII"))
- a->to_encode_in = "US-ASCII";
-
- if (a->word[0]=='<' && a->word[a->word_length-1]=='>') {
- a->type = wt_Address;
- }
-}
-
-static void
-break_into_words(const char *string, struct word_data *a, int words)
-{
- int i;
- for (i=0;i<words;i++) {
-
- char *next_space = strchr(string, ' ');
-
- if (!next_space) {
- a[i].word = string;
- a[i].word_length = strlen(string);
- a[i].to_encode_in = NULL; /* i.e. the default */
-
- show_entry(a+i);
-
- return;
- }
-
- a[i].word = string;
- a[i].word_length = next_space - string;
- a[i].to_encode_in = NULL;
-
- show_entry(a+i);
-
- string = next_space + 1;
-
- }
-}
-
-static void
-join_words(struct word_data *a, int words)
-{
- int i;
- for (i=(words-1);i>0;i--) {
- if (a[i].to_encode_in == a[i-1].to_encode_in) {
- a[i-1].word_length += 1 + a[i].word_length;
- a[i].word = 0;
- a[i].word_length = 0;
- }
-
- }
-}
-
-static void show_words(struct word_data *words, int count)
-{
- int i;
- for (i=0;i<count;i++)
- if (words[i].word)
- show_entry(words+i);
-}
-
-gchar *
-gmime_rfc2047_encode (const gchar *string, const gchar *charset)
-{
- int temp_len = strlen (string)*4 + 1, word_count;
- char *temp = g_malloc (temp_len), *temp_2 = temp;
- int string_length = strlen (string);
- char *encoded = NULL, *p;
- struct word_data *words;
-
- /* first, let us convert to UTF-8 */
- unicode_iconv_t i = unicode_iconv_open ("UTF-8", charset);
- unicode_iconv (i, &string, &string_length, &temp_2, &temp_len);
- unicode_iconv_close (i);
-
- /* null terminate it */
- *temp_2 = 0;
-
- /* now encode it as if it were a single word */
-
- word_count = words_in ( temp );
-
- words = g_malloc(sizeof (struct word_data) * word_count);
- break_into_words(temp, words, word_count);
-
- join_words(words, word_count);
-
- show_words(words, word_count);
-
- {
- size_t len = 0;
- int c = 0;
- for (c = 0;c<word_count;c++) {
- if (words[c].word)
- {
- words[c].encoded = encode_word(words[c].word, words[c].word_length,
- words[c].to_encode_in ? words[c].to_encode_in :
- "UTF-8");
- len += strlen(words[c].encoded) + 1;
- }
- }
-
- {
- encoded = g_malloc(len+1);
- p = encoded;
- for (c = 0; c < word_count;c++) if (words[c].word) {
- strcpy(p, words[c].encoded);
- p += strlen(p);
- strcpy(p, " ");
- p++;
- }
- *p = 0;
- }
- }
-
-
- /*
-
- real algorithm :
-
- we need to
-
- split it into words
-
- identify portions that have NOT to be encoded (i.e. <> and the comment starter/ender )
-
- identify the best character set for each word
-
- merge words which share a character set, allow jumping and merging with words which
- would be ok to encode in non-US-ASCII.
-
- if we have to use 2 character sets, try and collapse them into one.
-
- (e.g. if one word contains letters in latin-1, and another letters in latin-2, use
- latin-2 for the first word as well if possible).
-
- finally :
-
- if utf-8 will still be used, use it for everything.
-
- and then, at last, generate the encoded text, using base64/quoted-printable for
- each word depending upon which is more efficient.
-
- TODO :
- create a priority list of encodings
-
- i.e.
-
- US-ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-3, KOI8,
-
- Should survey for most popular charsets :
- what do people usually use for the following scripts?
-
- * Chinese/Japanese/Korean
- * Greek
- * Cyrillic
-
- (any other scripts commonly used in mail/news?)
-
- This algorithm is probably far from optimal, but should be
- reasonably efficient for simple cases. (and almost free if
- the text is just in US-ASCII : like 99% of the text that will
- pass through it)
-
-
-
- current status :
-
- Algorithm now partially implemented.
-
- */
-
- g_free(words);
- g_free(temp);
-
- return encoded;
-}
diff --git a/camel/gmime-rfc2047.h b/camel/gmime-rfc2047.h
deleted file mode 100644
index a4f1bc02a2..0000000000
--- a/camel/gmime-rfc2047.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* gmime-rfc2047.c: implemention of RFC2047 */
-
-/*
- * Author :
- * Bertrand Guiheneuf <bertrand@helixcode.com>
- *
- * Copyright 1999, 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
- *
- */
-
-/*
- * Authors: Robert Brady <rwb197@ecs.soton.ac.uk>
- */
-
-#ifndef GMIME_RFC2047_H
-#define GMIME_RFC2047_H 1
-#include <glib.h>
-
-gchar *gmime_rfc2047_decode (const gchar *text, const gchar* charset);
-gchar *gmime_rfc2047_encode (const gchar *text, const gchar* charset);
-
-/*
- * pass text and charset, (e.g. "UTF-8", or "ISO-8859-1"), and
- * it will encode or decode text according to RFC2047
- *
- * You will need to link with libunicode for these.
- *
- * TODO : Make it so that if charset==NULL, the charset specified (either
- * implicitly or explicity) in the locale is used.
- *
- * TODO : Run torture tests and fix the buffer overruns in these functions.
- *
- * The caller will need to free the memory for the string.
- */
-
-#endif /* GMIME_RFC2047_H */
diff --git a/camel/providers/Makefile.am b/camel/providers/Makefile.am
index 888a5d7bb3..7c520a8b8b 100644
--- a/camel/providers/Makefile.am
+++ b/camel/providers/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS = mbox pop3 sendmail smtp nntp
+SUBDIRS = mbox pop3 sendmail smtp
# these ones are disabled for the moment.
# MH maildir imap smtp
diff --git a/camel/providers/mbox/Makefile.am b/camel/providers/mbox/Makefile.am
index e3234ce228..4dbe3ed93d 100644
--- a/camel/providers/mbox/Makefile.am
+++ b/camel/providers/mbox/Makefile.am
@@ -24,22 +24,22 @@ libcamelmbox_la_SOURCES = \
camel-mbox-parser.c \
camel-mbox-provider.c \
camel-mbox-store.c \
- camel-mbox-summary.c \
camel-mbox-search.c \
+ camel-mbox-summary.c \
camel-mbox-utils.c
libcamelmboxinclude_HEADERS = \
camel-mbox-folder.h \
camel-mbox-parser.h \
camel-mbox-store.h \
- camel-mbox-summary.h \
camel-mbox-search.h \
+ camel-mbox-summary.h \
camel-mbox-utils.h
-
libcamelmbox_la_LDFLAGS = -version-info 0:0:0 -rpath $(libdir)
libcamelmbox_la_LIBADD = $(top_builddir)/e-util/libeutil.la $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS)
#libcamelmbox_la_LIBADD = $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS)
EXTRA_DIST =
+
diff --git a/camel/providers/mbox/camel-mbox-folder.c b/camel/providers/mbox/camel-mbox-folder.c
index 9cd055a658..2fa3ddc9da 100644
--- a/camel/providers/mbox/camel-mbox-folder.c
+++ b/camel/providers/mbox/camel-mbox-folder.c
@@ -2,9 +2,10 @@
/* camel-mbox-folder.c : Abstract class for an email folder */
/*
- * Author : Bertrand Guiheneuf <bertrand@helixcode.com>
+ * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
+ * Michael Zucchi <notzed@helixcode.com>
*
- * Copyright (C) 1999 Helix Code .
+ * Copyright (C) 1999, 2000 Helix Code Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -48,6 +49,8 @@
#include "camel-exception.h"
+#define d(x)
+
static CamelFolderClass *parent_class=NULL;
/* Returns the class for a CamelMboxFolder */
@@ -59,8 +62,6 @@ static CamelFolderClass *parent_class=NULL;
static void _init (CamelFolder *folder, CamelStore *parent_store,
CamelFolder *parent_folder, const gchar *name,
gchar separator, CamelException *ex);
-static void _set_name(CamelFolder *folder, const gchar *name, CamelException *ex);
-
static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex);
static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex);
@@ -80,6 +81,8 @@ static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, Ca
static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
#endif
+GPtrArray *summary_get_message_info (CamelFolder *folder, int first, int count);
+
static void _finalize (GtkObject *object);
static void
@@ -94,7 +97,6 @@ camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class)
/* virtual method overload */
camel_folder_class->init = _init;
- camel_folder_class->set_name = _set_name;
camel_folder_class->open = _open;
camel_folder_class->close = _close;
camel_folder_class->exists = _exists;
@@ -117,12 +119,12 @@ camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class)
camel_folder_class->search_complete = camel_mbox_folder_search_complete;
camel_folder_class->search_cancel = camel_mbox_folder_search_cancel;
+ camel_folder_class->get_message_info = summary_get_message_info;
+
gtk_object_class->finalize = _finalize;
}
-
-
static void
_finalize (GtkObject *object)
{
@@ -134,10 +136,6 @@ _finalize (GtkObject *object)
GTK_OBJECT_CLASS (parent_class)->finalize (object);
}
-
-
-
-
GtkType
camel_mbox_folder_get_type (void)
{
@@ -162,154 +160,73 @@ camel_mbox_folder_get_type (void)
return camel_mbox_folder_type;
}
-
-
-
-
-
static void
_init (CamelFolder *folder, CamelStore *parent_store,
CamelFolder *parent_folder, const gchar *name, gchar separator,
CamelException *ex)
{
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
+ const gchar *root_dir_path;
+
/* call parent method */
parent_class->init (folder, parent_store, parent_folder,
name, separator, ex);
- if (camel_exception_get_id (ex)) return;
+ if (camel_exception_get_id (ex))
+ return;
/* we assume that the parent init
method checks for the existance of @folder */
-
folder->can_hold_messages = TRUE;
folder->can_hold_folders = TRUE;
folder->has_summary_capability = TRUE;
folder->has_uid_capability = TRUE;
folder->has_search_capability = TRUE;
- folder->summary = NULL;
-}
-
+ mbox_folder->summary = NULL;
-/* internal method used to :
- - test for the existence of a summary file
- - test the sync between the summary and the mbox file
- - load the summary or create it if necessary
-*/
-static void
-_check_get_or_maybe_generate_summary_file (CamelMboxFolder *mbox_folder,
- CamelException *ex)
-{
- CamelFolder *folder = CAMEL_FOLDER (mbox_folder);
- CamelMboxSummary *summ;
- GArray *message_info_array;
- gint mbox_file_fd;
- guint32 next_uid;
- guint32 file_size;
- struct stat st;
-
- folder->summary = NULL;
-
- /* Test for the existence and up-to-dateness of the summary file. */
- if (access (mbox_folder->summary_file_path, F_OK) == 0) {
- summ = camel_mbox_summary_load (mbox_folder->summary_file_path,
- ex);
- if (summ) {
- if (stat (mbox_folder->folder_file_path, &st) == 0 &&
- summ->mbox_file_size == st.st_size &&
- summ->mbox_modtime == st.st_mtime)
- folder->summary = CAMEL_FOLDER_SUMMARY (summ);
- else
- gtk_object_destroy (GTK_OBJECT (summ));
- } else {
- /* Bad summary file */
- if (camel_exception_get_id (ex) !=
- CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID)
- return;
- camel_exception_clear (ex);
- }
- }
-
- /* In the case where the summary does not exist (or was the
- * wrong version), or is not in sync with the mbox file,
- * regenerate it.
- */
- if (folder->summary == NULL) {
- /* Parse the mbox folder and get some information
- * about the messages.
- */
- mbox_file_fd = open (mbox_folder->folder_file_path, O_RDONLY);
- if (mbox_file_fd != -1) {
- message_info_array =
- camel_mbox_parse_file (mbox_file_fd, "From ",
- 0, &file_size,
- &next_uid, TRUE,
- NULL, 0, ex);
- close (mbox_file_fd);
- if (camel_exception_get_id (ex))
- return;
-
- next_uid = camel_mbox_write_xev (mbox_folder,
- mbox_folder->folder_file_path,
- message_info_array,
- &file_size,
- next_uid, ex);
- if (camel_exception_get_id (ex)) {
- /* ** FIXME : free the preparsed information */
- return;
- }
+ /* now set the name info */
+ g_free (mbox_folder->folder_file_path);
+ g_free (mbox_folder->folder_dir_path);
+ g_free (mbox_folder->index_file_path);
- summ = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL));
- summ->message_info = parsed_information_to_mbox_summary (message_info_array);
- summ->nb_message = summ->message_info->len;
- summ->next_uid = next_uid;
- summ->mbox_file_size = file_size;
- /* **FIXME : Free the parsed information structure */
- } else {
- summ = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL));
- summ->message_info = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation));
- summ->nb_message = 0;
- summ->next_uid = 0;
- summ->mbox_file_size = 0;
- }
+ root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store));
- folder->summary = CAMEL_FOLDER_SUMMARY (summ);
- }
+ mbox_folder->folder_file_path = g_strdup_printf ("%s/%s", root_dir_path, folder->full_name);
+ mbox_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->full_name);
+ mbox_folder->folder_dir_path = g_strdup_printf ("%s/%s.sdb", root_dir_path, folder->full_name);
+ mbox_folder->index_file_path = g_strdup_printf ("%s/%s.ibex", root_dir_path, folder->full_name);
}
-
-
static void
_open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex)
{
CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
+ /* call parent class */
+ parent_class->open (folder, mode, ex);
+ if (camel_exception_get_id(ex))
+ return;
+
mbox_folder->index = ibex_open(mbox_folder->index_file_path, O_CREAT|O_RDWR, 0600);
if (mbox_folder->index == NULL) {
g_warning("Could not open/create index file: %s: indexing will not function",
strerror(errno));
}
- /* call parent class */
- parent_class->open (folder, mode, ex);
- if (camel_exception_get_id(ex))
+ mbox_folder->summary = camel_mbox_summary_new(mbox_folder->summary_file_path, mbox_folder->folder_file_path, mbox_folder->index);
+ if (mbox_folder->summary == NULL) {
+ camel_exception_set (ex,
+ CAMEL_EXCEPTION_FOLDER_INVALID, /* FIXME: right error code */
+ "Could not create summary");
return;
-
-#if 0
- /* get (or create) uid list */
- if (!(mbox_load_uid_list (mbox_folder) > 0))
- mbox_generate_uid_list (mbox_folder);
-#endif
-
- _check_get_or_maybe_generate_summary_file (mbox_folder, ex);
+ }
+ camel_mbox_summary_load(mbox_folder->summary);
}
-
static void
_close (CamelFolder *folder, gboolean expunge, CamelException *ex)
{
CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
- CamelMboxSummary *mbox_summary = CAMEL_MBOX_SUMMARY (folder->summary);
- struct stat st;
/* call parent implementation */
parent_class->close (folder, expunge, ex);
@@ -317,47 +234,15 @@ _close (CamelFolder *folder, gboolean expunge, CamelException *ex)
/* save index */
if (mbox_folder->index) {
ibex_close(mbox_folder->index);
+ mbox_folder->index = NULL;
}
-
- /* Update the summary and save it to disk */
- if (stat (mbox_folder->folder_file_path, &st) == 0) {
- mbox_summary->mbox_file_size = st.st_size;
- mbox_summary->mbox_modtime = st.st_mtime;
- }
- camel_mbox_summary_save (mbox_summary,
- mbox_folder->summary_file_path, ex);
-}
-
-
-
-
-static void
-_set_name (CamelFolder *folder, const gchar *name, CamelException *ex)
-{
- CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
- const gchar *root_dir_path;
-
- /* call default implementation */
- parent_class->set_name (folder, name, ex);
- if (camel_exception_get_id (ex)) return;
-
- g_free (mbox_folder->folder_file_path);
- g_free (mbox_folder->folder_dir_path);
- g_free (mbox_folder->index_file_path);
-
- root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store));
-
- mbox_folder->folder_file_path = g_strdup_printf ("%s/%s", root_dir_path, folder->full_name);
- mbox_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->full_name);
- mbox_folder->folder_dir_path = g_strdup_printf ("%s/%s.sdb", root_dir_path, folder->full_name);
- mbox_folder->index_file_path = g_strdup_printf ("%s/%s.ibex", root_dir_path, folder->full_name);
+ camel_mbox_summary_save (mbox_folder->summary);
+ camel_mbox_summary_unref (mbox_folder->summary);
+ mbox_folder->summary = NULL;
}
-
-
-
-
+/* FIXME: clean up this snot */
static gboolean
_exists (CamelFolder *folder, CamelException *ex)
{
@@ -420,18 +305,11 @@ _exists (CamelFolder *folder, CamelException *ex)
return exists;
}
-
-
-
-
-
-
-
+/* FIXME: clean up this snot */
static gboolean
_create (CamelFolder *folder, CamelException *ex)
{
CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
- CamelMboxSummary *summary;
const gchar *folder_file_path, *folder_dir_path;
mode_t dir_mode = S_IRWXU;
gint mkdir_error;
@@ -480,13 +358,6 @@ _create (CamelFolder *folder, CamelException *ex)
close (creat_fd);
- /* create the summary object */
- summary = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL));
- summary->nb_message = 0;
- summary->next_uid = 1;
- summary->mbox_file_size = 0;
- summary->message_info = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation));
-
return TRUE;
/* exception handling for io errors */
@@ -505,7 +376,7 @@ _create (CamelFolder *folder, CamelException *ex)
}
-
+/* FIXME: cleanup */
static gboolean
_delete (CamelFolder *folder, gboolean recurse, CamelException *ex)
{
@@ -605,9 +476,7 @@ _delete (CamelFolder *folder, gboolean recurse, CamelException *ex)
return TRUE;
}
-
-
-
+/* TODO: remove this */
gboolean
_delete_messages (CamelFolder *folder, CamelException *ex)
{
@@ -616,7 +485,6 @@ _delete_messages (CamelFolder *folder, CamelException *ex)
const gchar *folder_file_path;
gboolean folder_already_exists;
int creat_fd;
-
g_assert(folder!=NULL);
/* in the case where the folder does not exist,
@@ -667,7 +535,7 @@ _delete_messages (CamelFolder *folder, CamelException *ex)
}
-
+/* FIXME: cleanup */
static GList *
_list_subfolders (CamelFolder *folder, CamelException *ex)
{
@@ -786,138 +654,133 @@ _list_subfolders (CamelFolder *folder, CamelException *ex)
return NULL;
}
-
-
-
static gint
_get_message_count (CamelFolder *folder, CamelException *ex)
{
- gint message_count;
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
g_assert (folder);
- g_assert (folder->summary);
+ g_assert (mbox_folder->summary);
- message_count = CAMEL_MBOX_SUMMARY (folder->summary)->nb_message;
-
- return message_count;
+ return camel_mbox_summary_message_count(mbox_folder->summary);
}
+/*
+ This is a lazy append.
+ Basically, messages are appended to the end of the mbox, and probably assigned
+ a new uid (they wont be if copying from a source folder which doesn't have
+ a uid - which wont happen with the current summariser).
+
+ Indexing/summarising happens when the mbox is next queried.
+
+ Should this set a flag up for subsequent updating??
+*/
+
+/* FIXME: this may need some tweaking for performance? */
static void
_append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex)
{
- CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
- CamelMboxSummary *summary = CAMEL_MBOX_SUMMARY (folder->summary);
+ CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder), *source_folder;
CamelStream *output_stream;
- guint32 tmp_file_size;
- guint32 next_uid;
- gint tmp_file_fd;
- GArray *message_info_array;
- GArray *mbox_summary_info;
- gchar *tmp_message_filename;
- gint fd1, fd2;
- int i;
-
- tmp_message_filename = g_strdup_printf ("%s.tmp",
- mbox_folder->folder_file_path);
-
- /* write the message itself */
- output_stream = camel_stream_fs_new_with_name (tmp_message_filename,
- CAMEL_STREAM_FS_WRITE);
- if (output_stream != NULL) {
- camel_stream_write_string (output_stream, "From - \n");
- camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream);
- }
- camel_stream_close (output_stream);
- gtk_object_unref (GTK_OBJECT (output_stream));
-
- /* at this point we have saved the message to a
- temporary file, now, we have to add the x-evolution
- field and also update the main summary */
-
- /*
- First : parse the mbox file, but only from the
- position where the message has been added,
- wich happens to be the last postion in the
- mbox file before we added the message.
- This position is still stored in the summary
- for the moment
- */
- next_uid = summary->next_uid;
- tmp_file_fd = open (tmp_message_filename, O_RDONLY);
- message_info_array =
- camel_mbox_parse_file (tmp_file_fd, "From - ", 0,
- &tmp_file_size, &next_uid, TRUE,
- NULL, 0, ex);
-
- close (tmp_file_fd);
-
- /* get the value of the last available UID
- as saved in the summary file, again */
- next_uid = summary->next_uid;
-
- /* make sure all our of message info's have 0 uid - ignore any
- set elsewhere */
- for (i=0;i<message_info_array->len;i++) {
- g_array_index(message_info_array, CamelMboxParserMessageInfo, i).uid = 0;
- }
+ struct stat st;
+ off_t seek;
+ char *xev;
+ guint32 uid;
- /*
- OK, this is not very efficient, we should not use the same
- method as for parsing an entire mail file,
- but I have no time to write a simpler parser
- */
- next_uid = camel_mbox_write_xev (mbox_folder, tmp_message_filename,
- message_info_array, &tmp_file_size, next_uid, ex);
-
- if (camel_exception_get_id (ex)) {
- /* ** FIXME : free the preparsed information */
+ if (stat(mbox_folder->folder_file_path, &st) != 0) {
+ camel_exception_setv (ex,
+ CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */
+ "Cannot append to mbox file: %s", strerror (errno));
return;
}
- mbox_summary_info =
- parsed_information_to_mbox_summary (message_info_array);
+ /* are we coming from an mbox folder? then we can optimise somewhat ... */
+ if (message->folder && IS_CAMEL_MBOX_FOLDER(message->folder)) {
+ CamelMboxMessageInfo *info;
+ int sfd, dfd;
+ off_t pos;
- /* store the number of messages as well as the summary array */
- summary->nb_message += 1;
- summary->next_uid = next_uid;
+ /* FIXME: this is pretty ugly - we lookup the message info in the source folder, copy it,
+ then go back and paste in its real uid. */
+ source_folder = (CamelMboxFolder *)message->folder;
+ info = camel_mbox_summary_uid(source_folder->summary, message->message_uid);
- ((CamelMboxSummaryInformation *)(mbox_summary_info->data))->position +=
- summary->mbox_file_size;
- summary->mbox_file_size += tmp_file_size;
+ d(printf("Copying message directly from %s to %s\n", source_folder->folder_file_path, mbox_folder->folder_file_path));
+ d(printf("start = %d, xev = %d\n", ((CamelMboxMessageContentInfo *)info->info.content)->pos, info->xev_offset));
- camel_mbox_summary_append_entries (summary, mbox_summary_info);
- g_array_free (mbox_summary_info, TRUE);
-
+ sfd = open(source_folder->folder_file_path, O_RDONLY);
+ dfd = open(mbox_folder->folder_file_path, O_RDWR|O_CREAT, 0600);
+ if (lseek(dfd, st.st_size, SEEK_SET) != st.st_size) {
+ camel_exception_setv (ex,
+ CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */
+ "Cannot append to mbox file: %s", strerror (errno));
+ close(sfd);
+ close(dfd);
+ return;
+ }
+ write(dfd, "From - \n", strlen("From - \n"));
+ camel_mbox_summary_copy_block
+ (sfd, dfd, ((CamelMboxMessageContentInfo *)info->info.content)->pos,
+ ((CamelMboxMessageContentInfo *)info->info.content)->endpos - ((CamelMboxMessageContentInfo *)info->info.content)->pos);
+ if (info->xev_offset != -1) {
+ pos = st.st_size + (info->xev_offset - ((CamelMboxMessageContentInfo *)info->info.content)->pos) + strlen("From - \n");
+ d(printf("Inserting new uid at %d\n", (int)pos));
+ if (pos != lseek(dfd, pos, SEEK_SET)) {
+ ftruncate(dfd, st.st_size);
+ camel_exception_setv (ex,
+ CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */
+ "Cannot append to mbox file: %s", strerror (errno));
+ close(sfd);
+ close(dfd);
+ return;
+ }
+ uid = camel_mbox_summary_next_uid(mbox_folder->summary);
+ xev = g_strdup_printf("X-Evolution: %08x-%04x", uid, 0);
+ write(dfd, xev, strlen(xev)); /* FIXME: check return */
+ d(printf("header = %s\n", xev));
+ g_free(xev);
+ }
+ close(sfd);
+ close(dfd);
+ return;
+ }
- /* append the temporary file message to the mbox file */
- fd1 = open (tmp_message_filename, O_RDONLY);
- fd2 = open (mbox_folder->folder_file_path,
- O_WRONLY | O_CREAT | O_APPEND,
- 0600);
+ /* its not an mbox folder, so lets do it the slow way ... */
+ output_stream = camel_stream_fs_new_with_name (mbox_folder->folder_file_path, CAMEL_STREAM_FS_WRITE);
+ if (output_stream == NULL) {
+ camel_exception_setv (ex,
+ CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */
+ "Cannot append to mbox file: %s", strerror (errno));
+ return;
+ }
- if (fd2 == -1) {
+ seek = camel_seekable_stream_seek((CamelSeekableStream *)output_stream, st.st_size, SEEK_SET);
+ if (seek != st.st_size) {
camel_exception_setv (ex,
- CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
- "could not open the mbox folder file for appending the message\n"
- "\t%s\n"
- "Full error is : %s\n",
- mbox_folder->folder_file_path,
- strerror (errno));
+ CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */
+ "Cannot seek to position in mbox file: %s", strerror (errno));
+ gtk_object_unref ((GtkObject *)output_stream);
return;
}
- camel_mbox_copy_file_chunk (fd1,
- fd2,
- tmp_file_size,
- ex);
- close (fd1);
- close (fd2);
+ /* assign a new x-evolution header */
+ /* FIXME: save flags? */
+ camel_medium_remove_header((CamelMedium *)message, "X-Evolution");
+ uid = camel_mbox_summary_next_uid(mbox_folder->summary);
+ xev = g_strdup_printf("%08x-%04x", uid, 0);
+ camel_medium_add_header((CamelMedium *)message, "X-Evolution", xev);
+ g_free(xev);
+
+ camel_stream_write_string (output_stream, "From - \n");
+ /* FIXME: does this return an error? IT HAS TO FOR THIS TO BE RELIABLE */
+ camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream);
+ camel_stream_close (output_stream);
- /* remove the temporary file */
- unlink (tmp_message_filename);
+ /* TODO: update the summary so it knows a new message is there to summarise/index */
+ /* This is only a performance improvement, the summary is *only* a cache */
- g_free (tmp_message_filename);
+ gtk_object_unref (GTK_OBJECT (output_stream));
}
@@ -926,109 +789,95 @@ _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException
static GList *
_get_uid_list (CamelFolder *folder, CamelException *ex)
{
- GArray *message_info_array;
- CamelMboxSummaryInformation *message_info;
GList *uid_list = NULL;
- int i;
-
- message_info_array =
- CAMEL_MBOX_SUMMARY (folder->summary)->message_info;
-
- for (i=0; i<message_info_array->len; i++) {
- message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i;
- uid_list = g_list_prepend (uid_list, g_strdup_printf ("%u", message_info->uid));
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
+ int i, count;
+
+ /* FIXME: how are these allocated strings ever free'd? */
+ count = camel_mbox_summary_message_count(mbox_folder->summary);
+ for (i=0;i<count;i++) {
+ CamelMboxMessageInfo *info = camel_mbox_summary_index(mbox_folder->summary, i);
+ uid_list = g_list_prepend(uid_list, g_strdup(info->info.uid));
}
return uid_list;
}
-
-
-
static CamelMimeMessage *
_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex)
{
- GArray *message_info_array;
- CamelMboxSummaryInformation *message_info;
- char uidbuf[20];
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
+ CamelMboxMessageInfo *info;
- message_info_array =
- CAMEL_MBOX_SUMMARY (folder->summary)->message_info;
+ g_warning("YOUR CODE SHOULD NOT BE GETTING MESSAGES BY NUMBER, CHANGE IT");
- if (number > message_info_array->len) {
+ info = camel_mbox_summary_index(mbox_folder->summary, number);
+ if (info == NULL) {
camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
"No such message %d in folder `%s'.",
number, folder->name);
return NULL;
}
- message_info =
- (CamelMboxSummaryInformation *)(message_info_array->data) +
- (number - 1);
- sprintf (uidbuf, "%lu", message_info->uid);
-
- return _get_message_by_uid (folder, uidbuf, ex);
+ return _get_message_by_uid (folder, info->info.uid, ex);
}
-
static CamelMimeMessage *
_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex)
{
-
CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
- GArray *message_info_array;
- CamelMboxSummaryInformation *message_info = NULL;
- guint32 searched_uid;
- int i;
- gboolean uid_found;
CamelStream *message_stream;
CamelMimeMessage *message = NULL;
CamelStore *parent_store;
+ CamelMboxMessageInfo *info;
- searched_uid = strtoul (uid, NULL, 10);
-
- message_info_array =
- CAMEL_MBOX_SUMMARY (folder->summary)->message_info;
- i=0;
- uid_found = FALSE;
-
- /* first, look for the message that has the searched uid */
- while ((i<message_info_array->len) && (!uid_found)) {
- message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i;
- uid_found = (message_info->uid == searched_uid);
- i++;
+ /* get the parent store */
+ parent_store = camel_folder_get_parent_store (folder, ex);
+ if (camel_exception_get_id (ex)) {
+ return NULL;
}
-
- /* if the uid was not found, raise an exception and return */
- if (!uid_found) {
+
+ /* get the message summary info */
+ info = camel_mbox_summary_uid(mbox_folder->summary, uid);
+
+ if (info == NULL) {
camel_exception_setv (ex,
CAMEL_EXCEPTION_FOLDER_INVALID_UID,
"uid %s not found in the folder",
uid);
return NULL;
}
-
- /* at this point, the message_info structure
- contains the informations concerning the
- message that was searched for */
-
- /* create a stream bound to the message */
+
+ /* if this has no content, its an error in the library */
+ g_assert(info->info.content);
+
+ /* FIXME: more checks below */
+ /* create a stream bound to the message position/size */
message_stream = camel_stream_fs_new_with_name_and_bounds (mbox_folder->folder_file_path,
CAMEL_STREAM_FS_READ,
- message_info->position,
- message_info->position + message_info->size);
+ ((CamelMboxMessageContentInfo *)info->info.content)->pos,
+ ((CamelMboxMessageContentInfo *)info->info.content)->endpos);
+ message = camel_mime_message_new();
+ camel_data_wrapper_set_input_stream (CAMEL_DATA_WRAPPER (message), message_stream);
+ /* init other fields? */
+ message->folder = folder;
+ message->message_uid = g_strdup(uid);
- /* get the parent store */
- parent_store = camel_folder_get_parent_store (folder, ex);
- if (camel_exception_get_id (ex)) {
- gtk_object_unref (GTK_OBJECT (message_stream));
- return NULL;
- }
-
-
- message = camel_mime_message_new ();
- camel_data_wrapper_set_input_stream (CAMEL_DATA_WRAPPER (message), message_stream);
-
return message;
}
+
+/* get message info for a range of messages */
+GPtrArray *summary_get_message_info (CamelFolder *folder, int first, int count)
+{
+ GPtrArray *array = g_ptr_array_new();
+ int i, maxcount;
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
+
+ maxcount = camel_mbox_summary_message_count(mbox_folder->summary);
+ maxcount = MAX(count, maxcount);
+ for (i=first;i<maxcount;i++)
+ g_ptr_array_add(array, g_ptr_array_index(mbox_folder->summary->messages, i));
+
+ return array;
+}
diff --git a/camel/providers/mbox/camel-mbox-folder.h b/camel/providers/mbox/camel-mbox-folder.h
index 52d3fa6e70..f74c51a6c3 100644
--- a/camel/providers/mbox/camel-mbox-folder.h
+++ b/camel/providers/mbox/camel-mbox-folder.h
@@ -35,8 +35,8 @@ extern "C" {
#include <gtk/gtk.h>
#include "camel-folder.h"
-#include "camel-mbox-summary.h"
#include "libibex/ibex.h"
+#include "camel-mbox-summary.h"
/* #include "camel-store.h" */
@@ -45,7 +45,6 @@ extern "C" {
#define CAMEL_MBOX_FOLDER_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_MBOX_FOLDER_TYPE, CamelMboxFolderClass))
#define IS_CAMEL_MBOX_FOLDER(o) (GTK_CHECK_TYPE((o), CAMEL_MBOX_FOLDER_TYPE))
-
typedef struct {
CamelFolder parent_object;
@@ -54,11 +53,11 @@ typedef struct {
gchar *folder_dir_path; /* contains the subfolders */
gchar *index_file_path; /* index of body contents */
- GList *uid_array;
-
ibex *index; /* index for this folder */
int search_id; /* next search id */
GList *searches; /* current searches */
+
+ CamelMboxSummary *summary;
} CamelMboxFolder;
diff --git a/camel/providers/mbox/camel-mbox-search.c b/camel/providers/mbox/camel-mbox-search.c
index 1a584b8db6..1e134476e5 100644
--- a/camel/providers/mbox/camel-mbox-search.c
+++ b/camel/providers/mbox/camel-mbox-search.c
@@ -84,10 +84,9 @@ struct _searchcontext {
ibex *index; /* index of content for this folder */
#endif
- CamelFolderSummary *summary;
- const GArray *message_info;
+ CamelMboxSummary *summary;
- CamelMessageInfo *message_current; /* when performing a (match operation */
+ CamelMboxMessageInfo *message_current; /* when performing a (match operation */
};
struct _glib_sux_donkeys {
@@ -115,7 +114,7 @@ func_body_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void
if (ctx->index) {
for (i=0;i<argc && !truth;i++) {
if (argv[i]->type == ESEXP_RES_STRING) {
- truth = ibex_find_name(ctx->index, ctx->message_current->uid, argv[i]->value.string);
+ truth = ibex_find_name(ctx->index, ctx->message_current->info.uid, argv[i]->value.string);
} else {
g_warning("Invalid type passed to body-contains match function");
}
@@ -189,19 +188,19 @@ func_match_all(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
r = e_sexp_result_new(ESEXP_RES_ARRAY_PTR);
r->value.ptrarray = g_ptr_array_new();
- for (i=0;i<ctx->message_info->len;i++) {
+ for (i=0;i<ctx->summary->messages->len;i++) {
if (argc>0) {
- ctx->message_current = &g_array_index(ctx->message_info, CamelMessageInfo, i);
+ ctx->message_current = g_ptr_array_index(ctx->summary->messages, i);
r1 = e_sexp_term_eval(f, argv[0]);
if (r1->type == ESEXP_RES_BOOL) {
if (r1->value.bool)
- g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid);
+ g_ptr_array_add(r->value.ptrarray, ctx->message_current->info.uid);
} else {
g_warning("invalid syntax, matches require a single bool result");
}
e_sexp_result_free(r1);
} else {
- g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid);
+ g_ptr_array_add(r->value.ptrarray, ctx->message_current->info.uid);
}
}
ctx->message_current = NULL;
@@ -221,27 +220,28 @@ func_header_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, voi
/* are we inside a match-all? */
if (ctx->message_current && argc>1
&& argv[0]->type == ESEXP_RES_STRING) {
- char *headername, *header;
+ char *headername, *header = NULL;
+ char strbuf[32];
int i;
/* only a subset of headers are supported .. */
headername = argv[0]->value.string;
if (!strcasecmp(headername, "subject")) {
- header = ctx->message_current->subject;
+ header = ctx->message_current->info.subject;
} else if (!strcasecmp(headername, "date")) {
- header = ctx->message_current->sent_date;
+ sprintf(strbuf, "%d", (int)ctx->message_current->info.date_sent);
+ header = strbuf;
} else if (!strcasecmp(headername, "from")) {
- header = ctx->message_current->sender;
+ header = ctx->message_current->info.from;
} else {
g_warning("Performing query on unknown header: %s", headername);
- header = NULL;
}
if (header) {
for (i=1;i<argc && !truth;i++) {
if (argv[i]->type == ESEXP_RES_STRING
&& strstr(header, argv[i]->value.string)) {
- printf("%s got a match with %s of %s\n", ctx->message_current->uid, header, argv[i]->value.string);
+ printf("%s got a match with %s of %s\n", ctx->message_current->info.uid, header, argv[i]->value.string);
truth = TRUE;
break;
}
@@ -276,6 +276,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr
GList *matches = NULL;
ESExp *f;
ESExpResult *r;
+ CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder;
/* setup our expression evaluator */
f = e_sexp_new();
@@ -286,7 +287,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr
/* setup out context */
ctx->folder = folder;
- ctx->summary = camel_folder_get_summary(folder, ex);
+ ctx->summary = mbox_folder->summary;
if (ctx->summary == NULL || camel_exception_get_id (ex)) {
printf ("Cannot get summary\n"
@@ -296,10 +297,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr
return -1;
}
- gtk_object_ref((GtkObject *)ctx->summary);
-
/* FIXME: the index should be global to the folder */
- ctx->message_info = CAMEL_MBOX_SUMMARY(ctx->summary)->message_info;
ctx->message_current = NULL;
ctx->index = CAMEL_MBOX_FOLDER(folder)->index;
if (!ctx->index) {
@@ -337,7 +335,6 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr
printf("no result!\n");
}
- gtk_object_unref((GtkObject *)ctx->summary);
gtk_object_unref((GtkObject *)f);
i = ctx->id;
diff --git a/camel/providers/mbox/camel-mbox-summary.c b/camel/providers/mbox/camel-mbox-summary.c
index e15bb13b03..11f1c5779a 100644
--- a/camel/providers/mbox/camel-mbox-summary.c
+++ b/camel/providers/mbox/camel-mbox-summary.c
@@ -1,420 +1,1258 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-/*
- * Author : Bertrand Guiheneuf <bertrand@helixcode.com>
+/*
+ * Copyright (C) 2000 Helix Code Inc.
*
- * Copyright (C) 1999 - 2000 Helix Code .
-
- * 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.
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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.
+ * 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 Library 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
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
-#include <config.h>
-
-#include "camel-exception.h"
-#include "camel-mbox-folder.h"
-#include "camel-mbox-summary.h"
-#include "md5-utils.h"
-
-
-#include <sys/stat.h>
-#include <unistd.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <fcntl.h>
-#include <dirent.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
#include <stdio.h>
#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <camel/camel-mime-parser.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-filter-basic.h>
+#include <camel/camel-mime-filter-charset.h>
+#include <camel/camel-mime-filter-index.h>
+
+#include <camel/camel-mime-utils.h>
+
+#include "camel-mbox-summary.h"
+
#include <errno.h>
+#include <ctype.h>
#include <netinet/in.h>
-static CamelFolderSummaryClass *parent_class = NULL;
+#define d(x)
-static int count_messages (CamelFolderSummary *summary);
-static int count_subfolders (CamelFolderSummary *summary);
-static GPtrArray *get_subfolder_info (CamelFolderSummary *summary,
- int first, int count);
-static GPtrArray *get_message_info (CamelFolderSummary *summary,
- int first, int count);
-static void finalize (GtkObject *object);
+#define CAMEL_MBOX_SUMMARY_VERSION 2
-static void
-camel_mbox_summary_class_init (CamelMboxSummaryClass *camel_mbox_summary_class)
+static int safe_write(int fd, char *buffer, size_t towrite);
+static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info);
+
+/*
+ Disk file format?
+
+ message uid
+message-block
+ date:
+ date received?
+
+ subject: (unicode encoded)
+ from: (unicode encoded)
+ to: (unicode)
+
+ content-block
+
+content-block
+ content-type: ; params;
+ content-id:
+ content-description:
+ content-transfer-encoding:
+ message-start:
+ header-size:
+ body-size:
+
+ message-block
+ multipart-block
+
+ */
+
+/* pah, i dont care, its almost no code and it works, dont need a glist */
+struct _node {
+ struct _node *next;
+};
+
+static struct _node *
+my_list_append(struct _node **list, struct _node *n)
+{
+ struct _node *ln = (struct _node *)list;
+ while (ln->next)
+ ln = ln->next;
+ n->next = 0;
+ ln->next = n;
+ return n;
+}
+
+static int
+my_list_size(struct _node **list)
+{
+ int len = 0;
+ struct _node *ln = (struct _node *)list;
+ while (ln->next) {
+ ln = ln->next;
+ len++;
+ }
+ return len;
+}
+
+/* low-level io functions */
+static int
+encode_int (FILE *out, gint32 value)
+{
+ int i;
+
+ for (i=28;i>0;i-=7) {
+ if (value >= (1<<i)) {
+ unsigned int c = (value>>i) & 0x7f;
+ if (fputc(c, out) == -1)
+ return -1;
+ }
+ }
+ return fputc(value | 0x80, out);
+}
+
+static gint32
+decode_int (FILE *in)
{
- GtkObjectClass *gtk_object_class =
- GTK_OBJECT_CLASS (camel_mbox_summary_class);
- CamelFolderSummaryClass *camel_folder_summary_class =
- CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_class);
+ gint32 value=0, v;
- parent_class = gtk_type_class (camel_folder_summary_get_type ());
+ /* until we get the last byte, keep decoding 7 bits at a time */
+ while ( ((v = fgetc(in)) & 0x80) == 0 && v!=EOF) {
+ value |= v;
+ value <<= 7;
+ }
+ value |= (v&0x7f);
+ return value;
+}
- /* virtual method override */
- camel_folder_summary_class->count_messages = count_messages;
- camel_folder_summary_class->count_subfolders = count_subfolders;
- camel_folder_summary_class->get_subfolder_info = get_subfolder_info;
- camel_folder_summary_class->get_message_info = get_message_info;
+static int
+encode_fixed_int (FILE *out, gint32 value)
+{
+ guint32 save;
- gtk_object_class->finalize = finalize;
+ save = htonl(value);
+ return fwrite(&save, sizeof(save), 1, out);
}
+static gint32
+decode_fixed_int (FILE *out)
+{
+ guint32 save;
-GtkType
-camel_mbox_summary_get_type (void)
+ if (fread(&save, sizeof(save), 1, out) != -1) {
+ return ntohl(save);
+ } else {
+ return -1;
+ }
+}
+
+/* should be sorted, for binary search */
+/* This is a tokenisation mechanism for strings written to the
+ summary - to save space.
+ This list can have at most 31 words. */
+static char * tokens[] = {
+ "7bit",
+ "8bit",
+ "alternative",
+ "application",
+ "base64",
+ "boundary",
+ "charset",
+ "filename",
+ "html",
+ "image",
+ "iso-8859-1",
+ "iso-8859-8",
+ "message",
+ "mixed",
+ "multipart",
+ "name",
+ "octet-stream",
+ "parallel",
+ "plain",
+ "quoted-printable",
+ "rfc822",
+ "text",
+ "us-ascii", /* 23 words */
+};
+
+#define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
+
+/* baiscally ...
+ 0 = null
+ 1-tokens_len == tokens[id-1]
+ >=32 string, length = n-32
+*/
+
+static int
+encode_string (FILE *out, char *str)
{
- static GtkType camel_mbox_summary_type = 0;
+ if (str == NULL) {
+ return encode_int(out, 0);
+ } else {
+ int len = strlen(str);
+ int i, token=-1;
+
+ if (len <= 16) {
+ char lower[32];
+
+ for (i=0;i<len;i++)
+ lower[i] = tolower(str[i]);
+ lower[i] = 0;
+ for (i=0;i<tokens_len;i++) {
+ if (!strcmp(tokens[i], lower)) {
+ token = i;
+ break;
+ }
+ }
+ }
+ if (token != -1) {
+ return encode_int(out, token+1);
+ } else {
+ if (encode_int(out, len+32) == -1)
+ return -1;
+ return fwrite(str, len, 1, out);
+ }
+ }
+ return 0;
+}
- if (!camel_mbox_summary_type) {
- GtkTypeInfo camel_mbox_summary_info =
- {
- "CamelMboxSummary",
- sizeof (CamelMboxSummary),
- sizeof (CamelMboxSummaryClass),
- (GtkClassInitFunc) camel_mbox_summary_class_init,
- (GtkObjectInitFunc) NULL,
- /* reserved_1 */ NULL,
- /* reserved_2 */ NULL,
- (GtkClassInitFunc) NULL,
- };
+static char *
+decode_string (FILE *in)
+{
+ char *ret;
+ int len;
+
+ len = decode_int(in);
- camel_mbox_summary_type = gtk_type_unique (camel_folder_summary_get_type (), &camel_mbox_summary_info);
+ if (len<32) {
+ if (len <= 0) {
+ ret = NULL;
+ } else if (len<= tokens_len) {
+ ret = g_strdup(tokens[len-1]);
+ } else {
+ g_warning("Invalid token encountered: %d", len);
+ ret = NULL;
+ }
+ } else if (len > 10240) {
+ g_warning("Got broken string header length: %d bytes", len);
+ ret = NULL;
+ } else {
+ len -= 32;
+ ret = g_malloc(len+1);
+ if (fread(ret, len, 1, in) == -1) {
+ g_free(ret);
+ return NULL;
+ }
+ ret[len]=0;
}
- return camel_mbox_summary_type;
+ return ret;
}
+
+
+/* allocation functions */
+
static void
-finalize (GtkObject *object)
+body_part_dump(CamelMboxMessageContentInfo *bs, int depth)
{
- CamelMboxSummary *summary = CAMEL_MBOX_SUMMARY (object);
- CamelMboxSummaryInformation *info;
- int i;
+ CamelMboxMessageContentInfo *c;
+ char *prefix;
- for (i = 0; i < summary->message_info->len; i++) {
- info = &(((CamelMboxSummaryInformation *)summary->message_info->data)[i]);
- g_free (info->headers.subject);
- g_free (info->headers.sender);
- g_free (info->headers.to);
- g_free (info->headers.sent_date);
- g_free (info->headers.received_date);
- g_free (info->headers.uid);
+ if (bs == NULL)
+ return;
+
+ prefix = alloca(depth*2+1);
+ memset(prefix, ' ', depth*2);
+ prefix[depth*2]=0;
+ printf("%scontent-range: %d %d %d\n", prefix, (int)bs->pos, (int)bs->bodypos, (int)bs->endpos);
+ printf("%scontent-type: %s/%s\n", prefix, bs->info.type?bs->info.type->type:"?", bs->info.type?bs->info.type->subtype:"?");
+ printf("%scontent-id: %s\n", prefix, bs->info.id);
+ printf("%scontent-description: %s\n", prefix, bs->info.description);
+ printf("%scontent-transfer-encoding: %s\n", prefix, bs->info.encoding);
+ c = (CamelMboxMessageContentInfo *)bs->info.childs;
+ while (c) {
+ printf("%s -- \n", prefix);
+ body_part_dump(c, depth+1);
+ c = (CamelMboxMessageContentInfo *)c->info.next;
}
- g_array_free (summary->message_info, TRUE);
+}
- GTK_OBJECT_CLASS (parent_class)->finalize (object);
-}
+static void
+message_struct_dump(CamelMboxMessageInfo *ms)
+{
+ char *tmp;
-static int
-count_messages (CamelFolderSummary *summary)
+ if (ms == NULL) {
+ printf("Empty message?\n");
+ return;
+ }
+
+ printf("Subject: %s\n", ms->info.subject);
+ printf("From: %s\n", ms->info.from);
+ printf("To: %s\n", ms->info.to);
+ tmp = header_format_date(ms->info.date_sent, 0);
+ printf("Date: %s\n", tmp);
+ g_free(tmp);
+ tmp = header_format_date(ms->info.date_received, 0);
+ printf("Date-Received: %s\n", tmp);
+ g_free(tmp);
+ printf("UID: %08x-%04x\n", atoi(ms->info.uid), ms->info.flags);
+ printf(" -- content ->\n");
+ body_part_dump((CamelMboxMessageContentInfo *)ms->info.content, 1);
+}
+
+static CamelMboxMessageContentInfo *
+body_part_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body)
{
- return CAMEL_MBOX_SUMMARY (summary)->nb_message;
+ CamelMboxMessageContentInfo *bs;
+
+ bs = g_malloc0(sizeof(*bs));
+
+ bs->info.parent = (CamelMessageContentInfo *)parent;
+
+ bs->info.type = camel_mime_parser_content_type(mp);
+ header_content_type_ref(bs->info.type);
+
+ bs->info.id = header_msgid_decode(camel_mime_parser_header(mp, "content-id", NULL));
+ bs->info.description = header_decode_string(camel_mime_parser_header(mp, "content-description", NULL));
+ bs->info.encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
+
+ /* not sure what to set here? */
+ bs->pos = start;
+ bs->bodypos = body;
+ bs->endpos = -1;
+
+ if (parent)
+ my_list_append((struct _node **)&parent->info.childs, (struct _node *)bs);
+
+ return bs;
+}
+
+static CamelMboxMessageInfo *
+message_struct_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body, off_t xev_offset)
+{
+ CamelMboxMessageInfo *ms;
+
+ ms = g_malloc0(sizeof(*ms));
+
+ /* FIXME: what about cc, sender vs from? */
+ ms->info.subject = g_strdup(camel_mime_parser_header(mp, "subject", NULL));
+ ms->info.from = g_strdup(camel_mime_parser_header(mp, "from", NULL));
+ ms->info.to = g_strdup(camel_mime_parser_header(mp, "to", NULL));
+
+ ms->info.date_sent = header_decode_date(camel_mime_parser_header(mp, "date", NULL), NULL);
+ ms->info.date_received = 0;
+
+ ms->info.content = (CamelMessageContentInfo *)body_part_new(mp, parent, start, body);
+ ms->xev_offset = xev_offset;
+ return ms;
+}
+
+static void
+body_part_free(CamelMboxMessageContentInfo *bs)
+{
+ CamelMboxMessageContentInfo *c, *cn;
+
+ c = (CamelMboxMessageContentInfo *)bs->info.childs;
+ while (c) {
+ cn = (CamelMboxMessageContentInfo *)c->info.next;
+ body_part_free(c);
+ c = cn;
+ }
+ g_free(bs->info.id);
+ g_free(bs->info.description);
+ g_free(bs->info.encoding);
+ header_content_type_unref(bs->info.type);
+ g_free(bs);
+}
+
+static void
+message_struct_free(CamelMboxMessageInfo *ms)
+{
+ g_free(ms->info.subject);
+ g_free(ms->info.to);
+ g_free(ms->info.from);
+ body_part_free((CamelMboxMessageContentInfo *)ms->info.content);
+ g_free(ms);
+}
+
+
+/* IO functions */
+static CamelMboxMessageContentInfo *
+body_part_load(FILE *in)
+{
+ CamelMboxMessageContentInfo *bs = NULL, *c;
+ struct _header_content_type *ct;
+ char *type;
+ char *subtype;
+ int i, count;
+
+ d(printf("got content-block\n"));
+ bs = g_malloc0(sizeof(*bs));
+ bs->pos = decode_int(in);
+ bs->bodypos = bs->pos + decode_int(in);
+ bs->endpos = bs->pos + decode_int(in);
+
+ /* do content type */
+ d(printf("got content-type\n"));
+ type = decode_string(in);
+ subtype = decode_string(in);
+
+ ct = header_content_type_new(type, subtype);
+ bs->info.type = ct;
+ count = decode_int(in);
+ d(printf("getting %d params\n", count));
+ for (i=0;i<count;i++) {
+ char *name = decode_string(in);
+ char *value = decode_string(in);
+
+ d(printf(" %s = \"%s\"\n", name, value));
+
+ header_content_type_set_param(ct, name, value);
+ /* FIXME: do this so we dont have to double alloc/free */
+ g_free(name);
+ g_free(value);
+ }
+
+ d(printf("got content-id\n"));
+ bs->info.id = decode_string(in);
+ d(printf("got content-description\n"));
+ bs->info.description = decode_string(in);
+ d(printf("got content-encoding\n"));
+ bs->info.encoding = decode_string(in);
+
+ count = decode_int(in);
+ d(printf("got children, %d\n", count));
+ for (i=0;i<count;i++) {
+ c = body_part_load(in);
+ if (c) {
+ my_list_append((struct _node **)&bs->info.childs, (struct _node *)c);
+ c->info.parent = (CamelMessageContentInfo *)bs;
+ } else {
+ printf("Cannot load child\n");
+ }
+ }
+
+ return bs;
}
static int
-count_subfolders (CamelFolderSummary *summary)
+body_part_save(FILE *out, CamelMboxMessageContentInfo *bs)
{
- /* XXX */
- g_warning ("CamelMboxSummary::count_subfolders not implemented");
+ CamelMboxMessageContentInfo *c, *cn;
+ struct _header_content_type *ct;
+ struct _header_param *hp;
+
+ encode_int(out, bs->pos);
+ encode_int(out, bs->bodypos - bs->pos);
+ encode_int(out, bs->endpos - bs->pos);
+
+ ct = bs->info.type;
+ if (ct) {
+ encode_string(out, ct->type);
+ encode_string(out, ct->subtype);
+ encode_int(out, my_list_size((struct _node **)&ct->params));
+ hp = ct->params;
+ while (hp) {
+ encode_string(out, hp->name);
+ encode_string(out, hp->value);
+ hp = hp->next;
+ }
+ } else {
+ encode_string(out, NULL);
+ encode_string(out, NULL);
+ encode_int(out, 0);
+ }
+ encode_string(out, bs->info.id);
+ encode_string(out, bs->info.description);
+ encode_string(out, bs->info.encoding);
+
+ encode_int(out, my_list_size((struct _node **)&bs->info.childs));
+
+ c = (CamelMboxMessageContentInfo *)bs->info.childs;
+ while (c) {
+ cn = (CamelMboxMessageContentInfo *)c->info.next;
+ body_part_save(out, c);
+ c = cn;
+ }
+
return 0;
}
-static GPtrArray *
-get_subfolder_info (CamelFolderSummary *summary, int first, int count)
+static CamelMboxMessageInfo *
+message_struct_load(FILE *in)
{
- /* XXX */
- g_warning ("CamelMboxSummary::count_subfolders not implemented");
+ CamelMboxMessageInfo *ms;
+
+ ms = g_malloc0(sizeof(*ms));
+
+ ms->info.uid = g_strdup_printf("%u", decode_int(in));
+ ms->info.flags = decode_int(in);
+ ms->info.date_sent = decode_int(in);
+ ms->info.date_received = decode_int(in);
+ ms->xev_offset = decode_int(in);
+ ms->info.subject = decode_string(in);
+ ms->info.from = decode_string(in);
+ ms->info.to = decode_string(in);
+ ms->info.content = (CamelMessageContentInfo *)body_part_load(in);
+
+ return ms;
+}
+
+static int
+message_struct_save(FILE *out, CamelMboxMessageInfo *ms)
+{
+ encode_int(out, strtoul(ms->info.uid, NULL, 10));
+ encode_int(out, ms->info.flags);
+ encode_int(out, ms->info.date_sent);
+ encode_int(out, ms->info.date_received);
+ encode_int(out, ms->xev_offset);
+ encode_string(out, ms->info.subject);
+ encode_string(out, ms->info.from);
+ encode_string(out, ms->info.to);
+ body_part_save(out, (CamelMboxMessageContentInfo *)ms->info.content);
+
return 0;
}
-static GPtrArray *
-get_message_info (CamelFolderSummary *summary, int first, int count)
+static unsigned int
+header_evolution_decode(const char *in, unsigned int *uid, unsigned int *flags)
{
- CamelMboxSummary *mbox_summary = CAMEL_MBOX_SUMMARY (summary);
- CamelMboxSummaryInformation *info;
- GPtrArray *arr;
+ char *header;
+ if (in
+ && (header = header_decode_token(&in))) {
+ if (strlen(header) == strlen("00000000-0000")
+ && sscanf(header, "%08x-%04x", uid, flags) == 2) {
+ g_free(header);
+ return *uid;
+ }
+ g_free(header);
+ }
- /* XXX bounds check */
+ return ~0;
+}
+
+static int
+safe_write(int fd, char *buffer, size_t towrite)
+{
+ size_t donelen;
+ size_t len;
- arr = g_ptr_array_new ();
- for (; count; count--) {
- info = &((CamelMboxSummaryInformation *)mbox_summary->message_info->data)[first++];
- g_ptr_array_add (arr, info);
+ donelen = 0;
+ while (donelen < towrite) {
+ len = write(fd, buffer + donelen, towrite - donelen);
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ donelen += len;
}
+ return donelen;
+}
- return arr;
-}
-
-/**
- * camel_mbox_summary_save:
- * @summary:
- * @filename:
- * @ex:
- *
- * save the summary into a file
- **/
-void
-camel_mbox_summary_save (CamelMboxSummary *summary, const gchar *filename,
- CamelException *ex)
-{
- CamelMboxSummaryInformation *msg_info;
- guint cur_msg;
- guint field_length;
- gint fd;
- gint write_result; /* XXX use this */
- guint32 data;
-
- fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR);
- if (fd == -1) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
- "could not create the mbox summary "
- "file\n\t%s\nFull error is : %s\n",
- filename,
- strerror (errno));
- return;
+static int
+header_write(int fd, struct _header_raw *header, unsigned int uid, unsigned int flags)
+{
+ struct iovec iv[3];
+ int outlen = 0;
+
+ iv[1].iov_base = ":";
+ iv[1].iov_len = 1;
+
+ while (header) {
+ if (strcasecmp(header->name, "x-evolution")) {
+ int len;
+
+ iv[0].iov_base = header->name;
+ iv[0].iov_len = strlen(header->name);
+ iv[2].iov_base = header->value;
+ iv[2].iov_len = strlen(header->value);
+
+ do {
+ len = writev(fd, iv, 3);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -1;
+ outlen += len;
+ }
+ header = header->next;
}
- /* We write the file out in network byte order, not because
- * that makes sense, but because it's easy.
- */
-
- data = htonl (CAMEL_MBOX_SUMMARY_VERSION);
- write (fd, &data, sizeof (data));
-
- data = htonl (summary->nb_message);
- write (fd, &data, sizeof (data));
- data = htonl (summary->next_uid);
- write (fd, &data, sizeof (data));
- data = htonl (summary->mbox_file_size);
- write (fd, &data, sizeof (data));
- data = htonl (summary->mbox_modtime);
- write (fd, &data, sizeof (data));
-
- for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) {
- msg_info = (CamelMboxSummaryInformation *)
- (summary->message_info->data) + cur_msg;
-
- /* Write meta-info. */
- data = htonl (msg_info->position);
- write (fd, &data, sizeof (data));
- data = htonl (msg_info->size);
- write (fd, &data, sizeof (data));
- data = htonl (msg_info->x_evolution_offset);
- write (fd, &data, sizeof (data));
- data = htonl (msg_info->uid);
- write (fd, &data, sizeof (data));
- write (fd, &msg_info->status, 1);
-
- /* Write subject. */
- if (msg_info->headers.subject)
- field_length = strlen (msg_info->headers.subject);
- else
- field_length = 0;
- data = htonl (field_length);
- write (fd, &data, sizeof (data));
- if (msg_info->headers.subject)
- write (fd, msg_info->headers.subject, field_length);
-
- /* Write sender. */
- if (msg_info->headers.sender)
- field_length = strlen (msg_info->headers.sender);
- else
- field_length = 0;
- data = htonl (field_length);
- write (fd, &data, sizeof (data));
- if (msg_info->headers.sender)
- write (fd, msg_info->headers.sender, field_length);
-
- /* Write to. */
- if (msg_info->headers.to)
- field_length = strlen (msg_info->headers.to);
- else
- field_length = 0;
- data = htonl (field_length);
- write (fd, &data, sizeof (data));
- if (msg_info->headers.to)
- write (fd, msg_info->headers.to, field_length);
-
- /* Write sent date. */
- if (msg_info->headers.sent_date)
- field_length = strlen (msg_info->headers.sent_date);
- else
- field_length = 0;
- data = htonl (field_length);
- write (fd, &data, sizeof (data));
- if (msg_info->headers.sent_date)
- write (fd, msg_info->headers.sent_date, field_length);
-
- /* Write received date. */
- if (msg_info->headers.received_date)
- field_length = strlen (msg_info->headers.received_date);
+ return outlen;
+}
+
+/* returns -1 on error, else number of bytes written */
+int
+camel_mbox_summary_copy_block(int fromfd, int tofd, off_t readpos, size_t bytes)
+{
+ char buffer[4096];
+ int written = 0;
+ off_t pos, newpos;
+
+ pos = lseek(fromfd, 0, SEEK_CUR);
+ if (pos == -1)
+ return -1;
+
+ newpos = lseek(fromfd, readpos, SEEK_SET);
+ if (newpos == -1 || newpos != readpos)
+ goto error;
+
+ d(printf("oldpos = %d; copying %d from %d\n", (int)pos, (int)bytes, (int)readpos));
+
+ while (bytes>0) {
+ int toread, towrite, donelen;
+
+ toread = bytes;
+ if (bytes>4096)
+ toread = 4096;
else
- field_length = 0;
- data = htonl (field_length);
- write (fd, &data, sizeof (data));
- if (msg_info->headers.received_date)
- write (fd, msg_info->headers.received_date, field_length);
+ toread = bytes;
+ reread:
+ towrite = read(fromfd, buffer, toread);
+ if (towrite == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto reread;
+ goto error;
+ }
+
+ /* check for 'end of file' */
+ if (towrite == 0)
+ break;
+
+ if ( (donelen = safe_write(tofd, buffer, towrite)) == -1)
+ goto error;
+
+ written += donelen;
+ bytes -= donelen;
+ }
+
+ d(printf("written %d bytes\n", written));
+
+ newpos = lseek(fromfd, pos, SEEK_SET);
+ if (newpos == -1 || newpos != pos);
+ return -1;
+
+ return written;
+
+error:
+ lseek(fromfd, pos, SEEK_SET);
+ return -1;
+}
+
+#define SAVEIT
+
+static int index_folder(CamelMboxSummary *s, int startoffset)
+{
+ CamelMimeParser *mp;
+ int fd;
+ int fdout;
+ int state;
+
+ int toplevel = FALSE;
+ const char *xev;
+ char *data;
+ int datalen;
+
+ int enc_id=-1;
+ int chr_id=-1;
+ int idx_id=-1;
+ struct _header_content_type *ct;
+ int doindex=FALSE;
+ CamelMimeFilterCharset *mfc = NULL;
+ CamelMimeFilterIndex *mfi = NULL;
+ CamelMimeFilterBasic *mf64 = NULL, *mfqp = NULL;
+
+ CamelMboxMessageContentInfo *body = NULL, *parent = NULL;
+ CamelMboxMessageInfo *message = NULL;
+
+ int from_end = 0; /* start of message */
+ int from = 0; /* start of headers */
+ int last_write = 0; /* last written position */
+ int eof;
+ int write_offset = 0; /* how much does the dest differ from the source pos */
+ int old_offset = 0;
+
+ guint32 newuid;
+ off_t xevoffset = -1;
+
+ char *tmpname;
+
+ printf("indexing %s (%s) from %d\n", s->folder_path, s->summary_path, startoffset);
+
+ fd = open(s->folder_path, O_RDONLY);
+ if (fd==-1) {
+ perror("Can't open folder");
+ return -1;
}
- close (fd);
-}
-
-
-
-/**
- * camel_mbox_summary_load:
- * @filename:
- * @ex:
- *
- * load the summary from a file
- *
- * Return value:
- **/
-CamelMboxSummary *
-camel_mbox_summary_load (const gchar *filename, CamelException *ex)
-{
- CamelMboxSummaryInformation *msg_info;
- guint cur_msg;
- guint field_length;
- gint fd;
- CamelMboxSummary *summary;
- gint read_result;
- guint32 data;
-
- fd = open (filename, O_RDONLY);
- if (fd == -1) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
- "could not open the mbox summary file\n"
- "\t%s\nFull error is : %s\n",
- filename, strerror (errno));
- return NULL;
+ tmpname = g_strdup_printf("%s.tmp", s->folder_path);
+
+ fdout = open(tmpname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ if (fdout==-1) {
+ perror("Can't open output");
+ g_free(tmpname);
+ return -1;
}
- /* Verify version number. */
- read (fd, &data, sizeof(data));
- data = ntohl (data);
-
- if (data != CAMEL_MBOX_SUMMARY_VERSION) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID,
- "This folder summary was written by "
- "%s version of this software.",
- data < CAMEL_MBOX_SUMMARY_VERSION ?
- "an older" : "a newer");
- return NULL;
+ mp = camel_mime_parser_new();
+ camel_mime_parser_init_with_fd(mp, fd);
+ camel_mime_parser_scan_from(mp, TRUE);
+
+ /* FIXME: cleaner fail code */
+ if (startoffset > 0) {
+ if (camel_mime_parser_seek(mp, startoffset, SEEK_SET) != startoffset) {
+ g_free(tmpname);
+ gtk_object_unref((GtkObject *)mp);
+ return -1;
+ }
}
- summary = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL));
-
- read (fd, &data, sizeof(data));
- summary->nb_message = ntohl (data);
- read (fd, &data, sizeof(data));
- summary->next_uid = ntohl (data);
- read (fd, &data, sizeof(data));
- summary->mbox_file_size = ntohl (data);
- read (fd, &data, sizeof(data));
- summary->mbox_modtime = ntohl (data);
-
- summary->message_info =
- g_array_new (FALSE, FALSE,
- sizeof (CamelMboxSummaryInformation));
- g_array_set_size (summary->message_info, summary->nb_message);
-
- for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) {
- msg_info = (CamelMboxSummaryInformation *)
- (summary->message_info->data) + cur_msg;
-
- /* Read the meta-info. */
- read (fd, &data, sizeof(data));
- msg_info->position = ntohl (data);
- read (fd, &data, sizeof(data));
- msg_info->size = ntohl (data);
- read (fd, &data, sizeof(data));
- msg_info->x_evolution_offset = ntohl (data);
- read (fd, &data, sizeof(data));
- msg_info->uid = ntohl (data);
- msg_info->headers.uid = g_strdup_printf ("%d", msg_info->uid);
- read (fd, &msg_info->status, 1);
-
- /* Read the subject. */
- read (fd, &field_length, sizeof (field_length));
- field_length = ntohl (field_length);
- if (field_length > 0) {
- msg_info->headers.subject =
- g_new0 (gchar, field_length + 1);
- read (fd, msg_info->headers.subject, field_length);
- } else
- msg_info->headers.subject = NULL;
-
- /* Read the sender. */
- read (fd, &field_length, sizeof (field_length));
- field_length = ntohl (field_length);
- if (field_length > 0) {
- msg_info->headers.sender =
- g_new0 (gchar, field_length + 1);
- read (fd, msg_info->headers.sender, field_length);
- } else
- msg_info->headers.sender = NULL;
-
- /* Read the "to" field. */
- read (fd, &field_length, sizeof (field_length));
- field_length = ntohl (field_length);
- if (field_length > 0) {
- msg_info->headers.to =
- g_new0 (gchar, field_length + 1);
- read (fd, msg_info->headers.to, field_length);
- } else
- msg_info->headers.to = NULL;
-
- /* Read the sent date field. */
- read (fd, &field_length, sizeof (field_length));
- field_length = ntohl (field_length);
- if (field_length > 0) {
- msg_info->headers.sent_date =
- g_new0 (gchar, field_length + 1);
- read (fd, msg_info->headers.sent_date, field_length);
- } else
- msg_info->headers.sent_date = NULL;
-
- /* Read the received date field. */
- read (fd, &field_length, sizeof (field_length));
- field_length = ntohl (field_length);
- if (field_length > 0) {
- msg_info->headers.received_date =
- g_new0 (gchar, field_length + 1);
- read (fd, msg_info->headers.received_date,
- field_length);
- } else
- msg_info->headers.received_date = NULL;
- }
-
- close (fd);
- return summary;
-}
-
-
-/**
- * camel_mbox_summary_append_entries:
- * @summary:
- * @entries:
- *
- * append an entry to a summary
- **/
-void
-camel_mbox_summary_append_entries (CamelMboxSummary *summary, GArray *entries)
-{
-
- summary->message_info = g_array_append_vals (summary->message_info,
- entries->data,
- entries->len);
+ mfi = camel_mime_filter_index_new_ibex(s->index);
+
+ while ( (state = camel_mime_parser_step(mp, &data, &datalen)) != HSCAN_EOF ) {
+ switch(state) {
+ case HSCAN_FROM: /* starting a new message content */
+ /* save the current position */
+ d(printf("from = %d\n", (int)camel_mime_parser_tell(mp)));
+ toplevel = FALSE;
+ from = camel_mime_parser_tell(mp);
+ break;
+
+ case HSCAN_FROM_END:
+ d(printf("from-end = %d\n", (int)camel_mime_parser_tell(mp)));
+ d(printf("message from %d to %d\n", from_end, (int)camel_mime_parser_tell(mp)));
+ from_end = camel_mime_parser_tell(mp);
+ break;
+
+ case HSCAN_MESSAGE:
+ case HSCAN_MULTIPART:
+ case HSCAN_HEADER: /* starting a new header */
+ newuid=~0;
+ if (!toplevel) {
+ char name[32];
+ unsigned int olduid, oldflags;
+ int headerlen;
+ int docopy = FALSE;
+
+ /* check for X-Evolution header ... if its there, nothing to do (skip content) */
+ xev = camel_mime_parser_header(mp, "x-evolution", &xevoffset);
+ if (xev) {
+ d(printf("An x-evolution header exists at: %d = %s\n", xevoffset + write_offset, xev));
+ xevoffset = xevoffset + write_offset;
+ if (header_evolution_decode(xev, &olduid, &oldflags) != ~0) {
+ d(printf(" uid = %d = %x\n", olduid, olduid));
+ newuid = olduid;
+#if 0
+ while (camel_mime_parser_step(mp, &data, &datalen) != HSCAN_FROM_END)
+ ;
+ break;
+#endif
+ } else {
+ printf("Invalid xev header? I need to write out a new one ...\n");
+ }
+ }
+
+ toplevel = TRUE;
+
+ /* assign a new uid for this message */
+ if (newuid == ~0) {
+ newuid = s->nextuid++;
+ docopy = TRUE;
+ } else {
+ /* make sure we account for this uid when assigning uid's */
+ /* this really needs a pre-scan pass ... *sigh* */
+ camel_mbox_summary_set_uid(s, newuid);
+ }
+
+ /* setup index name for this uid */
+ sprintf(name, "%x", newuid);
+ camel_mime_filter_index_set_name(mfi, name);
+ /* remove all references to this name from the index */
+ if (s->index)
+ ibex_unindex(s->index, name);
+
+ d(printf("Message content starts at %d\n", camel_mime_parser_tell(mp)));
+
+ if (docopy) {
+ /* now, copy over bits of mbox from last write, and insert the X-Evolution header (at the top of headers) */
+ /* if we already have a valid x-evolution header, use that, dont need to copy */
+ camel_mbox_summary_copy_block(fd, fdout, last_write, from-last_write);
+ last_write = from;
+
+ headerlen = header_write(fdout, camel_mime_parser_headers_raw(mp), newuid, 0);
+ sprintf(name, "X-Evolution: %08x-%04x\n\n", newuid, 0);
+ safe_write(fdout, name, strlen(name));
+ d(printf("new X-Evolution at %d\n", headerlen + from + write_offset));
+ xevoffset = headerlen + from + write_offset;
+ old_offset = write_offset;
+
+ write_offset += (headerlen - (camel_mime_parser_tell(mp)-from)) + strlen(name);
+ last_write = camel_mime_parser_tell(mp);
+ }
+ } else {
+ old_offset = write_offset;
+ }
+
+ /* we only care about the rest for actual content parts */
+ /* TODO: Cleanup, this is a huge mess */
+ if (state != HSCAN_HEADER) {
+ if (message == NULL) {
+ message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset);
+ parent = (CamelMboxMessageContentInfo *)message->info.content;
+ if (newuid != ~0) {
+ message->info.uid = g_strdup_printf("%u", newuid);
+ } else {
+ g_warning("This shouldn't happen?");
+ }
+ } else {
+ parent = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset);
+ }
+ break;
+ }
+
+ if (message == NULL) {
+ message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset);
+ body = (CamelMboxMessageContentInfo *)message->info.content;
+ if (newuid != ~0) {
+ message->info.uid = g_strdup_printf("%u", newuid);
+ } else {
+ g_warning("This shouldn't happen?");
+ }
+ } else {
+ body = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset);
+ }
+
+ /* check headers for types that we can index */
+ ct = camel_mime_parser_content_type(mp);
+ if (header_content_type_is(ct, "text", "*")) {
+ char *encoding;
+ const char *charset;
+
+ /* TODO: The filters should all be cached, so they aren't recreated between
+ messages/message parts */
+ encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
+ if (encoding) {
+ if (!strcasecmp(encoding, "base64")) {
+ d(printf("Adding decoding filter for base64\n"));
+ if (mf64 == NULL)
+ mf64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+ enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mf64);
+ } else if (!strcasecmp(encoding, "quoted-printable")) {
+ d(printf("Adding decoding filter for quoted-printable\n"));
+ if (mfqp == NULL)
+ mfqp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC);
+ enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfqp);
+ }
+ g_free(encoding);
+ }
+
+ charset = header_content_type_param(ct, "charset");
+ if (charset!=NULL
+ && !(strcasecmp(charset, "us-ascii")==0
+ || strcasecmp(charset, "utf-8")==0)) {
+ d(printf("Adding conversion filter from %s to utf-8\n", charset));
+ if (mfc == NULL)
+ mfc = camel_mime_filter_charset_new_convert(charset, "utf-8");
+ if (mfc) {
+ chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc);
+ } else {
+ g_warning("Cannot convert '%s' to 'utf-8', message display may be corrupt", charset);
+ }
+ }
+
+ doindex = TRUE;
+
+ /* and this filter actually does the indexing */
+ idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfi);
+ } else {
+ doindex = FALSE;
+ }
+ break;
+
+ /* fixme, this needs thought *sigh* */
+ case HSCAN_MESSAGE_END:
+ case HSCAN_MULTIPART_END:
+ if (parent) {
+ parent->endpos = camel_mime_parser_tell(mp)+write_offset;
+ if (parent->info.parent == NULL) {
+ camel_mbox_summary_add(s, message);
+ message = NULL;
+ parent = NULL;
+ } else {
+ parent = (CamelMboxMessageContentInfo *)parent->info.parent;
+ }
+ }
+ break;
+
+ case HSCAN_BODY:
+ if (doindex) {
+ d(printf("Got content to index:\n%.*s", datalen, data));
+ }
+ break;
+
+ case HSCAN_BODY_END:
+ if (body) {
+ body->endpos = camel_mime_parser_tell(mp)+write_offset;
+ if (body->info.parent == NULL) {
+ camel_mbox_summary_add(s, message);
+ message = NULL;
+ }
+ }
+
+ d(printf("end of content, removing decoders\n"));
+ if (enc_id != -1) {
+ camel_mime_parser_filter_remove(mp, enc_id);
+ enc_id = -1;
+ }
+ if (chr_id != -1) {
+ camel_mime_parser_filter_remove(mp, chr_id);
+ chr_id = -1;
+ }
+ if (idx_id != -1) {
+ camel_mime_parser_filter_remove(mp, idx_id);
+ idx_id = -1;
+ }
+ break;
+ }
+ }
+
+ /* did we actually write anything out? Then rename and be done with it. */
+ if (last_write>0) {
+ eof = camel_mime_parser_tell(mp);
+ camel_mbox_summary_copy_block(fd, fdout, last_write, eof-last_write);
+
+ if (close(fdout) == -1) {
+ perror("Could not close output file");
+ unlink(tmpname);
+ } else {
+ printf("renaming %s to %s\n", tmpname, s->folder_path);
+ if (rename(tmpname, s->folder_path) == -1) {
+ perror("Error renaming file");
+ unlink(tmpname);
+ }
+ }
+ } else {
+ /* no, then dont bother touching the inbox */
+ printf("No written changes to mbox, removing tmp file\n");
+ close(fdout);
+ unlink(tmpname);
+ }
+
+ close(fd);
+
+ if (mf64) gtk_object_unref((GtkObject *)mf64);
+ if (mfqp) gtk_object_unref((GtkObject *)mfqp);
+ if (mfc) gtk_object_unref((GtkObject *)mfc);
+ if (mfi) gtk_object_unref((GtkObject *)mfi);
+
+ /* force an index sync? */
+ if (s->index) {
+ ibex_write(s->index);
+ }
+
+ gtk_object_unref((GtkObject *)mp);
+
+ /* and finally ... update the summary sync info */
+ {
+ struct stat st;
+
+ if (stat(s->folder_path, &st) == 0) {
+ s->time = st.st_mtime;
+ s->size = st.st_size;
+ }
+ }
+
+ g_free(tmpname);
+
+ return 0;
+}
+
+CamelMboxSummary *camel_mbox_summary_new(const char *summary, const char *folder, ibex *index)
+{
+ CamelMboxSummary *s;
+
+ s = g_malloc0(sizeof(*s));
+
+ s->dirty = TRUE;
+ s->folder_path = g_strdup(folder);
+ s->summary_path = g_strdup(summary);
+ /* FIXME: refcount index? */
+ s->index = index;
+
+ s->messages = g_ptr_array_new();
+ s->message_uid = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* always force an update */
+ s->time = 0;
+ s->size = 0;
+
+ s->nextuid = 1;
+
+ /* TODO: force an initial load right now? */
+
+ return s;
+}
+
+void camel_mbox_summary_unref(CamelMboxSummary *s)
+{
+ g_warning("Unimplemented function, mbox_summary_unref");
+}
+
+/* check that the summary is uptodate, TRUE means it is uptodate */
+int camel_mbox_summary_check(CamelMboxSummary *s)
+{
+ struct stat st;
+
+ /* no folder at all? */
+ if (stat(s->folder_path, &st) != 0)
+ return FALSE;
+
+ return (st.st_size == s->size) && (st.st_mtime == s->time);
+}
+
+static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info)
+{
+ if (info->info.uid == NULL) {
+ info->info.uid = g_strdup_printf("%u", s->nextuid++);
+ }
+ if (g_hash_table_lookup(s->message_uid, info->info.uid)) {
+ g_error("Trying to insert message with clashing uid's");
+ }
+ d(printf("adding %s\n", info->info.uid));
+ g_ptr_array_add(s->messages, info);
+ g_hash_table_insert(s->message_uid, info->info.uid, info);
+ s->dirty = TRUE;
+}
+
+static int summary_header_read(FILE *fp, guint32 *version, time_t *time, size_t *size, guint32 *nextuid)
+{
+ fseek(fp, 0, SEEK_SET);
+ *version = decode_fixed_int(fp);
+ *time = decode_fixed_int(fp);
+ *size = decode_fixed_int(fp);
+ *nextuid = decode_fixed_int(fp);
+ return ferror(fp);
+}
+
+static void
+summary_clear(CamelMboxSummary *s)
+{
+ int i;
+
+ for (i=0;i<s->messages->len;i++) {
+ message_struct_free(g_ptr_array_index(s->messages, i));
+ }
+ g_ptr_array_free(s->messages, TRUE);
+ g_hash_table_destroy(s->message_uid);
+
+ s->messages = g_ptr_array_new();
+ s->message_uid = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+int camel_mbox_summary_load(CamelMboxSummary *s)
+{
+ struct stat st;
+ FILE *fp;
+ int i, total;
+ CamelMboxMessageInfo *info;
+
+ summary_clear(s);
+
+ if ((fp = fopen(s->summary_path, "r")) == NULL) {
+ g_warning("Loading non-existant summary, generating summary for folder: %s: %s", s->summary_path, strerror(errno));
+ index_folder(s, 0);
+ camel_mbox_summary_save(s);
+ } else {
+ guint32 version, nextuid;
+ time_t time;
+ size_t size;
+
+ if (stat(s->folder_path, &st) != 0) {
+ g_warning("Uh, no folder anyway, aborting");
+ fclose(fp);
+ return -1;
+ }
+
+ if (summary_header_read(fp, &version, &time, &size, &nextuid) != 0
+ || version != CAMEL_MBOX_SUMMARY_VERSION) {
+ g_warning("Summary missing or version mismatch, reloading summary");
+ fclose(fp);
+ index_folder(s, 0);
+ camel_mbox_summary_save(s);
+ return 0;
+ }
+
+ s->nextuid = MAX(s->nextuid, nextuid);
+ s->time = time;
+ s->size = size;
+ total = decode_fixed_int(fp);
+
+ if (time != st.st_mtime || size != st.st_size) {
+ /* if its grown, then just index the new stuff, and load the rest from the summary */
+ if (size < st.st_size) {
+ g_warning("Indexing/summarizing from start position: %d", size);
+
+ d(printf("loading %d items from summary file\n", total));
+ for (i=0;i<total;i++) {
+ info = message_struct_load(fp);
+ if (info) {
+ camel_mbox_summary_add(s, info);
+ } else {
+ break;
+ }
+ }
+ fclose(fp);
+ s->dirty = FALSE;
+ index_folder(s, size); /* if it adds any, it'll dirtify it */
+ camel_mbox_summary_save(s);
+ } else {
+ g_warning("Folder changed/smaller, reindexing everything");
+ index_folder(s, 0);
+ camel_mbox_summary_save(s);
+ fclose(fp);
+ }
+ return 0;
+ }
+
+ printf("loading %d items from summary file\n", total);
+ for (i=0;i<total;i++) {
+ info = message_struct_load(fp);
+ if (info) {
+ camel_mbox_summary_add(s, info);
+ } else {
+ break;
+ }
+ }
+ fclose(fp);
+ s->dirty = FALSE;
+ }
+ return 0;
+}
+
+static int summary_header_write(FILE *fp, CamelMboxSummary *s)
+{
+ fseek(fp, 0, SEEK_SET);
+ encode_fixed_int(fp, CAMEL_MBOX_SUMMARY_VERSION);
+ encode_fixed_int(fp, s->time);
+ /* if we're dirty, then dont *really* save it ... */
+ if (s->dirty)
+ encode_fixed_int(fp, 0);
+ else
+ encode_fixed_int(fp, s->size);
+ encode_fixed_int(fp, s->nextuid);
+ fflush(fp);
+ return ferror(fp);
+}
+
+static int summary_header_save(CamelMboxSummary *s)
+{
+ int fd;
+ FILE *fp;
+
+ fd = open(s->summary_path, O_WRONLY|O_CREAT, 0600);
+ if (fd == -1)
+ return -1;
+ fp = fdopen(fd, "w");
+ if (fp == NULL)
+ return -1;
+
+ summary_header_write(fp, s);
+ return fclose(fp);
+}
+
+int camel_mbox_summary_save(CamelMboxSummary *s)
+{
+ int i, fd;
+ FILE *fp;
+
+ printf("saving summary? %s\n", s->summary_path);
+
+ /* FIXME: error checking */
+ if (s->dirty) {
+ printf("yes\n");
+ fd = open(s->summary_path, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ if (fd == -1)
+ return -1;
+ fp = fdopen(fd, "w");
+ if (fp == NULL)
+ return -1;
+
+ s->dirty = FALSE;
+
+ summary_header_write(fp, s);
+ encode_fixed_int(fp, s->messages->len);
+
+ printf("message count = %d\n", s->messages->len);
+
+ for (i=0;i<s->messages->len;i++) {
+ message_struct_save(fp, g_ptr_array_index(s->messages, i));
+ }
+ fclose(fp);
+ } else {
+ printf("no\n");
+ }
+ return 0;
+}
+
+CamelMboxMessageInfo *camel_mbox_summary_uid(CamelMboxSummary *s, const char *uid)
+{
+ return g_hash_table_lookup(s->message_uid, uid);
+}
+
+CamelMboxMessageInfo *camel_mbox_summary_index(CamelMboxSummary *s, int index)
+{
+ return g_ptr_array_index(s->messages, index);
+}
+
+int camel_mbox_summary_message_count(CamelMboxSummary *s)
+{
+ return s->messages->len;
+}
+
+guint32 camel_mbox_summary_next_uid(CamelMboxSummary *s)
+{
+ guint32 uid = s->nextuid++;
+
+ summary_header_save(s);
+ return uid;
+}
+
+guint32 camel_mbox_summary_set_uid(CamelMboxSummary *s, guint32 uid)
+{
+ if (s->nextuid < uid) {
+ s->nextuid = uid;
+ summary_header_save(s);
+ }
+ return s->nextuid;
+}
+
+#if 0
+int main(int argc, char **argv)
+{
+ if (argc<2) {
+ printf("usage: %s mbox\n", argv[0]);
+ return 1;
+ }
+
+ gtk_init(&argc, &argv);
+
+ index_folder(argv[1]);
+
+ return 0;
}
+#endif
diff --git a/camel/providers/mbox/camel-mbox-summary.h b/camel/providers/mbox/camel-mbox-summary.h
index f8570f15c0..80b59ef54f 100644
--- a/camel/providers/mbox/camel-mbox-summary.h
+++ b/camel/providers/mbox/camel-mbox-summary.h
@@ -1,85 +1,78 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-/*
- *
- * Author : Bertrand Guiheneuf <bertrand@helixcode.com>
+/*
+ * Copyright (C) 2000 Helix Code Inc.
*
- * Copyright (C) 1999 Helix Code (http://www.helixcode.com).
+ * Authors: Michael Zucchi <notzed@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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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.
+ * 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 Library 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
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef MBOX_SUMMARY_H
-#define MBOX_SUMMARY_H 1
-
-#include <camel-folder-summary.h>
-
-#define CAMEL_MBOX_SUMMARY_TYPE (camel_mbox_summary_get_type ())
-#define CAMEL_MBOX_SUMMARY(obj) (GTK_CHECK_CAST((obj), CAMEL_MBOX_SUMMARY_TYPE, CamelMboxSummary))
-#define CAMEL_MBOX_SUMMARY_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_MBOX_SUMMARY_TYPE, CamelMboxSummaryClass))
-#define CAMEL_IS_MBOX_SUMMARY(o) (GTK_CHECK_TYPE((o), CAMEL_MBOX_SUMMARY_TYPE))
-
-
-#define CAMEL_MBOX_SUMMARY_VERSION 1
+#ifndef _CAMEL_MBOX_SUMMARY_H
+#define _CAMEL_MBOX_SUMMARY_H
+#include <glib.h>
+#include <camel/camel-folder.h>
+#include <libibex/ibex.h>
typedef struct {
- CamelMessageInfo headers;
+ CamelMessageContentInfo info;
- guint32 position;
- guint size;
- guint x_evolution_offset;
- guint32 uid;
- guchar status;
+ /* position in stream of this part */
+ off_t pos;
+ off_t bodypos;
+ off_t endpos;
+} CamelMboxMessageContentInfo;
-} CamelMboxSummaryInformation;
-
-
-/* this contains informations about the whole mbox file */
typedef struct {
- CamelFolderSummary parent_object;
+ CamelMessageInfo info;
- guint nb_message; /* number of messages in the summary */
- guint32 next_uid;
- guint32 mbox_file_size;
- guint32 mbox_modtime;
+ /* position of the xev header, if one exists */
+ off_t xev_offset;
+} CamelMboxMessageInfo;
- GArray *message_info; /* array of CamelMboxSummaryInformation */
+typedef struct {
+ int dirty; /* if anything has changed */
-} CamelMboxSummary;
+ char *folder_path;
+ char *summary_path;
+ ibex *index;
-typedef struct {
- CamelFolderSummaryClass parent_class;
+ GPtrArray *messages; /* array of messages matching mbox order */
+ GHashTable *message_uid; /* index to messages by uid */
-} CamelMboxSummaryClass;
+ int nextuid;
+ time_t time; /* time/size of folder's last update */
+ size_t size;
+} CamelMboxSummary;
-GtkType camel_mbox_summary_get_type (void);
+CamelMboxSummary *camel_mbox_summary_new(const char *summary, const char *folder, ibex *index);
+void camel_mbox_summary_unref(CamelMboxSummary *);
-void camel_mbox_summary_save (CamelMboxSummary *summary,
- const gchar *filename, CamelException *ex);
-CamelMboxSummary *camel_mbox_summary_load (const gchar *filename,
- CamelException *ex);
+int camel_mbox_summary_load(CamelMboxSummary *);
+int camel_mbox_summary_save(CamelMboxSummary *);
+int camel_mbox_summary_check(CamelMboxSummary *);
-gboolean camel_mbox_summary_check_sync (gchar *summary_filename,
- gchar *mbox_filename,
- CamelException *ex);
+guint32 camel_mbox_summary_next_uid(CamelMboxSummary *);
+/* set the minimum uid */
+guint32 camel_mbox_summary_set_uid(CamelMboxSummary *s, guint32 uid);
-void camel_mbox_summary_append_entries (CamelMboxSummary *summary,
- GArray *entries);
+CamelMboxMessageInfo *camel_mbox_summary_uid(CamelMboxSummary *s, const char *uid);
+CamelMboxMessageInfo *camel_mbox_summary_index(CamelMboxSummary *, int index);
+int camel_mbox_summary_message_count(CamelMboxSummary *);
+/* TODO: should be in a utility library */
+int camel_mbox_summary_copy_block(int fromfd, int tofd, off_t readpos, size_t bytes);
-#endif /* MBOX_SUMMARY_H */
+#endif /* ! _CAMEL_MBOX_SUMMARY_H */
diff --git a/camel/providers/mbox/camel-mbox-utils.c b/camel/providers/mbox/camel-mbox-utils.c
index cd7da089e9..31710468b6 100644
--- a/camel/providers/mbox/camel-mbox-utils.c
+++ b/camel/providers/mbox/camel-mbox-utils.c
@@ -520,55 +520,3 @@ camel_mbox_write_xev (CamelMboxFolder *folder,
g_free (tmp_file_name_secure);
return next_free_uid;
}
-
-
-
-
-
-
-GArray *
-parsed_information_to_mbox_summary (GArray *parsed_information)
-{
- guint cur_msg;
- CamelMboxParserMessageInfo *cur_msg_info;
- GArray *mbox_summary;
- CamelMboxSummaryInformation *cur_sum_info;
-
- mbox_summary = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation));
- mbox_summary = g_array_set_size (mbox_summary, parsed_information->len);
-
- for (cur_msg = 0; cur_msg < parsed_information->len; cur_msg++) {
-
- cur_msg_info = (CamelMboxParserMessageInfo *)(parsed_information->data) + cur_msg;
- cur_sum_info = (CamelMboxSummaryInformation *)(mbox_summary->data) + cur_msg;
-
- cur_sum_info->position = cur_msg_info->message_position;
-
- cur_sum_info->size = cur_msg_info->size;
-
- cur_sum_info->x_evolution_offset = cur_msg_info->x_evolution_offset;
-
- cur_sum_info->uid = cur_msg_info->uid;
- cur_sum_info->headers.uid = g_strdup_printf ("%d",
- cur_sum_info->uid);
-
- cur_sum_info->status = cur_msg_info->status;
-
- cur_sum_info->headers.subject = cur_msg_info->subject;
- cur_msg_info->subject = NULL;
-
- cur_sum_info->headers.sender = cur_msg_info->from;
- cur_msg_info->from = NULL;
-
- cur_sum_info->headers.to = cur_msg_info->to;
- cur_msg_info->to = NULL;
-
- /* XXX I'm guessing one of these is wrong. */
- cur_sum_info->headers.received_date = cur_msg_info->date;
- cur_sum_info->headers.sent_date = g_strdup (cur_msg_info->date);
- cur_msg_info->date = NULL;
-
- }
-
- return mbox_summary;
-}