aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers
diff options
context:
space:
mode:
authorMichael Zucci <zucchi@src.gnome.org>2000-11-15 11:04:12 +0800
committerMichael Zucci <zucchi@src.gnome.org>2000-11-15 11:04:12 +0800
commit1593182b1ca1a76dbf65b5ba1700f1f0d56e097d (patch)
treee90d940af1e2250a92f13ca06358dbc4881d5cee /camel/providers
parentd266df61cc2439fc72aede6042bf2acf743ee2ee (diff)
downloadgsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar.gz
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar.bz2
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar.lz
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar.xz
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.tar.zst
gsoc2013-evolution-1593182b1ca1a76dbf65b5ba1700f1f0d56e097d.zip
Initial cut for local provider, to handle mh/mailbox/maildir at
least. Checking in to make a backup. svn path=/trunk/; revision=6575
Diffstat (limited to 'camel/providers')
-rw-r--r--camel/providers/local/Makefile.am48
-rw-r--r--camel/providers/local/camel-local-folder.c637
-rw-r--r--camel/providers/local/camel-local-folder.h97
-rw-r--r--camel/providers/local/camel-local-provider.c64
-rw-r--r--camel/providers/local/camel-local-store.c211
-rw-r--r--camel/providers/local/camel-local-store.h68
-rw-r--r--camel/providers/local/camel-local-summary.c516
-rw-r--r--camel/providers/local/camel-local-summary.h81
-rw-r--r--camel/providers/local/camel-mbox-folder.c322
-rw-r--r--camel/providers/local/camel-mbox-folder.h62
-rw-r--r--camel/providers/local/camel-mbox-provider.c59
-rw-r--r--camel/providers/local/camel-mbox-store.c180
-rw-r--r--camel/providers/local/camel-mbox-store.h59
-rw-r--r--camel/providers/local/camel-mbox-summary.c856
-rw-r--r--camel/providers/local/camel-mbox-summary.h63
-rw-r--r--camel/providers/local/camel-mh-folder.c204
-rw-r--r--camel/providers/local/camel-mh-folder.h62
-rw-r--r--camel/providers/local/camel-mh-store.c142
-rw-r--r--camel/providers/local/camel-mh-store.h59
-rw-r--r--camel/providers/local/camel-mh-summary.c320
-rw-r--r--camel/providers/local/camel-mh-summary.h53
-rw-r--r--camel/providers/local/libcamellocal.urls2
22 files changed, 4165 insertions, 0 deletions
diff --git a/camel/providers/local/Makefile.am b/camel/providers/local/Makefile.am
new file mode 100644
index 0000000000..01a3072cb9
--- /dev/null
+++ b/camel/providers/local/Makefile.am
@@ -0,0 +1,48 @@
+## Process this file with automake to produce Makefile.in
+
+libcamellocalincludedir = $(includedir)/camel
+
+providerdir = $(pkglibdir)/camel-providers/$(VERSION)
+
+provider_LTLIBRARIES = libcamellocal.la
+provider_DATA = libcamellocal.urls
+
+INCLUDES = -I.. \
+ -I$(srcdir)/.. \
+ -I$(top_srcdir)/camel \
+ -I$(top_srcdir)/intl \
+ -I$(top_srcdir)/libibex \
+ -I$(top_srcdir)/e-util \
+ -I$(top_srcdir) \
+ -I$(includedir) \
+ $(GTK_INCLUDEDIR) \
+ -DG_LOG_DOMAIN=\"camel-local-provider\"
+
+libcamellocal_la_SOURCES = \
+ camel-local-folder.c \
+ camel-local-store.c \
+ camel-local-summary.c \
+ camel-local-provider.c \
+ camel-mh-folder.c \
+ camel-mh-store.c \
+ camel-mh-summary.c \
+ camel-mbox-folder.c \
+ camel-mbox-store.c \
+ camel-mbox-summary.c
+
+libcamellocalinclude_HEADERS = \
+ camel-local-folder.h \
+ camel-local-store.h \
+ camel-local-summary.h \
+ camel-mh-folder.h \
+ camel-mh-store.h \
+ camel-mh-summary.h \
+ camel-mbox-folder.h \
+ camel-mbox-store.h \
+ camel-mbox-summary.h
+
+libcamellocal_la_LDFLAGS = -version-info 0:0:0
+
+libcamellocal_la_LIBADD = $(top_builddir)/e-util/libeutil.la $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS)
+
+EXTRA_DIST = libcamellocal.urls
diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c
new file mode 100644
index 0000000000..034f91d7ee
--- /dev/null
+++ b/camel/providers/local/camel-local-folder.c
@@ -0,0 +1,637 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
+/* camel-local-folder.c : Abstract class for an email folder */
+
+/*
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * 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
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "camel-local-folder.h"
+#include "camel-local-store.h"
+#include "string-utils.h"
+#include "camel-stream-fs.h"
+#include "camel-local-summary.h"
+#include "camel-data-wrapper.h"
+#include "camel-mime-message.h"
+#include "camel-stream-filter.h"
+#include "camel-mime-filter-from.h"
+#include "camel-exception.h"
+
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+static CamelFolderClass *parent_class = NULL;
+
+/* Returns the class for a CamelLocalFolder */
+#define CLOCALF_CLASS(so) CAMEL_LOCAL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CLOCALS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+
+static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex);
+static gint local_get_message_count(CamelFolder *folder);
+static gint local_get_unread_message_count(CamelFolder *folder);
+
+static GPtrArray *local_get_uids(CamelFolder *folder);
+static GPtrArray *local_get_summary(CamelFolder *folder);
+#if 0
+static void local_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex);
+static CamelMimeMessage *local_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex);
+#endif
+static void local_expunge(CamelFolder *folder, CamelException *ex);
+
+static const CamelMessageInfo *local_get_message_info(CamelFolder *folder, const char *uid);
+
+static GPtrArray *local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
+static void local_search_free(CamelFolder *folder, GPtrArray * result);
+
+static guint32 local_get_message_flags(CamelFolder *folder, const char *uid);
+static void local_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
+static gboolean local_get_message_user_flag(CamelFolder *folder, const char *uid, const char *name);
+static void local_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value);
+static const char *local_get_message_user_tag(CamelFolder *folder, const char *uid, const char *name);
+static void local_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value);
+
+
+static void local_finalize(CamelObject * object);
+
+static void
+camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class)
+{
+ CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_local_folder_class);
+
+ parent_class = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs(camel_folder_get_type()));
+
+ /* virtual method definition */
+
+ /* virtual method overload */
+ camel_folder_class->sync = local_sync;
+ camel_folder_class->get_message_count = local_get_message_count;
+ camel_folder_class->get_unread_message_count = local_get_unread_message_count;
+ camel_folder_class->get_uids = local_get_uids;
+ camel_folder_class->free_uids = camel_folder_free_deep;
+ camel_folder_class->get_summary = local_get_summary;
+ camel_folder_class->free_summary = camel_folder_free_nop;
+ camel_folder_class->expunge = local_expunge;
+
+ camel_folder_class->search_by_expression = local_search_by_expression;
+ camel_folder_class->search_free = local_search_free;
+
+ camel_folder_class->get_message_info = local_get_message_info;
+
+ camel_folder_class->get_message_flags = local_get_message_flags;
+ camel_folder_class->set_message_flags = local_set_message_flags;
+ camel_folder_class->get_message_user_flag = local_get_message_user_flag;
+ camel_folder_class->set_message_user_flag = local_set_message_user_flag;
+ camel_folder_class->get_message_user_tag = local_get_message_user_tag;
+ camel_folder_class->set_message_user_tag = local_set_message_user_tag;
+}
+
+static void
+local_init(gpointer object, gpointer klass)
+{
+ CamelFolder *folder = object;
+ CamelLocalFolder *local_folder = object;
+
+ folder->has_summary_capability = TRUE;
+ folder->has_search_capability = TRUE;
+
+ folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_DRAFT |
+ CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER;
+
+ local_folder->summary = NULL;
+ local_folder->search = NULL;
+}
+
+static void
+local_finalize(CamelObject * object)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(object);
+
+ if (local_folder->index)
+ ibex_close(local_folder->index);
+
+ g_free(local_folder->base_path);
+ g_free(local_folder->folder_path);
+ g_free(local_folder->summary_path);
+ g_free(local_folder->index_path);
+
+ camel_folder_change_info_free(local_folder->changes);
+}
+
+CamelType camel_local_folder_get_type(void)
+{
+ static CamelType camel_local_folder_type = CAMEL_INVALID_TYPE;
+
+ if (camel_local_folder_type == CAMEL_INVALID_TYPE) {
+ camel_local_folder_type = camel_type_register(CAMEL_FOLDER_TYPE, "CamelLocalFolder",
+ sizeof(CamelLocalFolder),
+ sizeof(CamelLocalFolderClass),
+ (CamelObjectClassInitFunc) camel_local_folder_class_init,
+ NULL,
+ (CamelObjectInitFunc) local_init,
+ (CamelObjectFinalizeFunc) local_finalize);
+ }
+
+ return camel_local_folder_type;
+}
+
+CamelLocalFolder *
+camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex)
+{
+ CamelFolder *folder;
+ const char *root_dir_path, *name;
+ struct stat st;
+ int forceindex;
+
+ folder = (CamelFolder *)lf;
+
+ name = strrchr(full_name, '/');
+ if (name)
+ name++;
+ else
+ name = full_name;
+
+ camel_folder_construct(folder, parent_store, full_name, name);
+
+ root_dir_path = camel_local_store_get_toplevel_dir(CAMEL_LOCAL_STORE(folder->parent_store));
+
+ lf->base_path = g_strdup(root_dir_path);
+ lf->folder_path = g_strdup_printf("%s/%s", root_dir_path, full_name);
+ lf->summary_path = g_strdup_printf("%s/%s.ev-summary", root_dir_path, full_name);
+ lf->index_path = g_strdup_printf("%s/%s.ibex", root_dir_path, full_name);
+
+ lf->changes = camel_folder_change_info_new();
+
+ /* if we have no index file, force it */
+ forceindex = stat(lf->index_path, &st) == -1;
+ if (flags & CAMEL_STORE_FOLDER_BODY_INDEX) {
+
+ lf->index = ibex_open(lf->index_path, O_CREAT | O_RDWR, 0600);
+ if (lf->index == NULL) {
+ /* yes, this isn't fatal at all */
+ g_warning("Could not open/create index file: %s: indexing not performed", strerror(errno));
+ forceindex = FALSE;
+ /* record that we dont have an index afterall */
+ flags &= ~CAMEL_STORE_FOLDER_BODY_INDEX;
+ }
+ } else {
+ /* if we do have an index file, remove it */
+ if (forceindex == FALSE) {
+ unlink(lf->index_path);
+ }
+ forceindex = FALSE;
+ }
+
+ lf->flags = flags;
+
+ lf->summary = CLOCALF_CLASS(lf)->create_summary(lf->summary_path, lf->folder_path, lf->index);
+ if (camel_local_summary_load(lf->summary, forceindex, ex) == -1) {
+ camel_object_unref (CAMEL_OBJECT (folder));
+ return NULL;
+ }
+
+ return lf;
+}
+
+/* Have to work out how/when to lock */
+int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex)
+{
+ return 0;
+}
+
+int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex)
+{
+ return 0;
+}
+
+static void
+local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
+{
+ CamelLocalFolder *lf = CAMEL_LOCAL_FOLDER(folder);
+
+ d(printf("local sync, expunge=%s\n", expunge?"true":"false"));
+
+ if (camel_local_folder_lock(lf, ex) == -1)
+ return;
+
+ camel_local_summary_sync(lf->summary, expunge, lf->changes, ex);
+ if (camel_folder_change_info_changed(lf->changes)) {
+ camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", lf->changes);
+ camel_folder_change_info_clear(lf->changes);
+ }
+
+ if (camel_local_folder_unlock(lf, ex) == -1)
+ return;
+
+ /* force save of metadata */
+ if (lf->index)
+ ibex_save(lf->index);
+ if (lf->summary)
+ camel_folder_summary_save(CAMEL_FOLDER_SUMMARY(lf->summary));
+}
+
+static void
+local_expunge(CamelFolder *folder, CamelException *ex)
+{
+ d(printf("expunge\n"));
+
+ /* Just do a sync with expunge, serves the same purpose */
+ camel_folder_sync(folder, TRUE, ex);
+}
+
+#if 0
+static void
+local_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+ CamelStream *output_stream = NULL, *filter_stream = NULL;
+ CamelMimeFilter *filter_from = NULL;
+ CamelMessageInfo *newinfo;
+ struct stat st;
+ off_t seek = -1;
+ char *xev;
+ guint32 uid;
+ char *fromline = NULL;
+
+ if (stat(local_folder->folder_path, &st) != 0)
+ goto fail;
+
+ output_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_WRONLY|O_APPEND, 0600);
+ if (output_stream == NULL)
+ goto fail;
+
+ seek = st.st_size;
+
+ /* assign a new x-evolution header/uid */
+ camel_medium_remove_header(CAMEL_MEDIUM(message), "X-Evolution");
+ uid = camel_folder_summary_next_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary));
+ /* important that the header matches exactly 00000000-0000 */
+ xev = g_strdup_printf("%08x-%04x", uid, info ? info->flags & 0xFFFF : 0);
+ camel_medium_add_header(CAMEL_MEDIUM(message), "X-Evolution", xev);
+ g_free(xev);
+
+ /* we must write this to the non-filtered stream ... */
+ fromline = camel_local_summary_build_from(CAMEL_MIME_PART(message)->headers);
+ if (camel_stream_printf(output_stream, seek==0?"%s":"\n%s", fromline) == -1)
+ goto fail;
+
+ /* and write the content to the filtering stream, that translated '\nFrom' into '\n>From' */
+ filter_stream = (CamelStream *) camel_stream_filter_new_with_stream(output_stream);
+ filter_from = (CamelMimeFilter *) camel_mime_filter_from_new();
+ camel_stream_filter_add((CamelStreamFilter *) filter_stream, filter_from);
+ if (camel_data_wrapper_write_to_stream(CAMEL_DATA_WRAPPER(message), filter_stream) == -1)
+ goto fail;
+
+ if (camel_stream_close(filter_stream) == -1)
+ goto fail;
+
+ /* filter stream ref's the output stream itself, so we need to unref it too */
+ camel_object_unref(CAMEL_OBJECT(filter_from));
+ camel_object_unref(CAMEL_OBJECT(filter_stream));
+ camel_object_unref(CAMEL_OBJECT(output_stream));
+ g_free(fromline);
+
+ /* force a summary update - will only update from the new position, if it can */
+ if (camel_local_summary_update(local_folder->summary, seek==0?seek:seek+1, local_folder->changes) == 0) {
+ char uidstr[16];
+
+ sprintf(uidstr, "%u", uid);
+ newinfo = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uidstr);
+
+ if (info && newinfo) {
+ CamelFlag *flag = info->user_flags;
+ CamelTag *tag = info->user_tags;
+
+ while (flag) {
+ camel_flag_set(&(newinfo->user_flags), flag->name, TRUE);
+ flag = flag->next;
+ }
+
+ while (tag) {
+ camel_tag_set(&(newinfo->user_tags), tag->name, tag->value);
+ tag = tag->next;
+ }
+ }
+ camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", local_folder->changes);
+ camel_folder_change_info_clear(local_folder->changes);
+ }
+
+ return;
+
+ fail:
+ if (camel_exception_is_set(ex)) {
+ camel_exception_setv(ex, camel_exception_get_id(ex),
+ _("Cannot append message to local file: %s"), camel_exception_get_description(ex));
+ } else {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot append message to local file: %s"), g_strerror(errno));
+ }
+ if (filter_stream) {
+ /*camel_stream_close (filter_stream); */
+ camel_object_unref(CAMEL_OBJECT(filter_stream));
+ }
+ if (output_stream)
+ camel_object_unref(CAMEL_OBJECT(output_stream));
+
+ if (filter_from)
+ camel_object_unref(CAMEL_OBJECT(filter_from));
+
+ g_free(fromline);
+
+ /* make sure the file isn't munged by us */
+ if (seek != -1) {
+ int fd = open(local_folder->folder_file_path, O_WRONLY, 0600);
+
+ if (fd != -1) {
+ ftruncate(fd, st.st_size);
+ close(fd);
+ }
+ }
+}
+
+static CamelMimeMessage *
+local_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+ CamelStream *message_stream = NULL;
+ CamelMimeMessage *message = NULL;
+ CamelLocalMessageInfo *info;
+ CamelMimeParser *parser = NULL;
+ char *buffer;
+ int len;
+
+ /* get the message summary info */
+ info = (CamelLocalMessageInfo *) camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uid);
+
+ if (info == NULL) {
+ errno = ENOENT;
+ goto fail;
+ }
+
+ /* if this has no content, its an error in the library */
+ g_assert(info->info.content);
+ g_assert(info->frompos != -1);
+
+ /* where we read from */
+ message_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_RDONLY, 0);
+ if (message_stream == NULL)
+ goto fail;
+
+ /* we use a parser to verify the message is correct, and in the correct position */
+ parser = camel_mime_parser_new();
+ camel_mime_parser_init_with_stream(parser, message_stream);
+ camel_object_unref(CAMEL_OBJECT(message_stream));
+ camel_mime_parser_scan_from(parser, TRUE);
+
+ camel_mime_parser_seek(parser, info->frompos, SEEK_SET);
+ if (camel_mime_parser_step(parser, &buffer, &len) != HSCAN_FROM) {
+ g_warning("File appears truncated");
+ goto fail;
+ }
+
+ if (camel_mime_parser_tell_start_from(parser) != info->frompos) {
+ /* TODO: This should probably perform a re-sync/etc, and try again? */
+ g_warning("Summary doesn't match the folder contents! eek!\n"
+ " expecting offset %ld got %ld", (long int)info->frompos,
+ (long int)camel_mime_parser_tell_start_from(parser));
+ errno = EINVAL;
+ goto fail;
+ }
+
+ message = camel_mime_message_new();
+ if (camel_mime_part_construct_from_parser(CAMEL_MIME_PART(message), parser) == -1) {
+ g_warning("Construction failed");
+ goto fail;
+ }
+ camel_object_unref(CAMEL_OBJECT(parser));
+
+ return message;
+
+ fail:
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s"), g_strerror(errno));
+
+ if (parser)
+ camel_object_unref(CAMEL_OBJECT(parser));
+ if (message)
+ camel_object_unref(CAMEL_OBJECT(message));
+
+ return NULL;
+}
+#endif
+
+/*
+ The following functions all work off the summary, so the default operations provided
+ in camel-local-folder will suffice for all subclasses. They may want to
+ snoop various operations to ensure the status remains synced, or just wait
+ for the sync operation
+*/
+static gint
+local_get_message_count(CamelFolder *folder)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+
+ g_return_val_if_fail(local_folder->summary != NULL, -1);
+
+ return camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary));
+}
+
+static gint
+local_get_unread_message_count(CamelFolder *folder)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+ CamelMessageInfo *info;
+ GPtrArray *infolist;
+ gint i, max, count = 0;
+
+ g_return_val_if_fail(local_folder->summary != NULL, -1);
+
+ max = camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary));
+ if (max == -1)
+ return -1;
+
+ infolist = local_get_summary(folder);
+
+ for (i = 0; i < infolist->len; i++) {
+ info = (CamelMessageInfo *) g_ptr_array_index(infolist, i);
+ if (!(info->flags & CAMEL_MESSAGE_SEEN))
+ count++;
+ }
+
+ return count;
+}
+
+static GPtrArray *
+local_get_uids(CamelFolder *folder)
+{
+ GPtrArray *array;
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+ int i, count;
+
+ count = camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary));
+ array = g_ptr_array_new();
+ g_ptr_array_set_size(array, count);
+ for (i = 0; i < count; i++) {
+ CamelMessageInfo *info = camel_folder_summary_index(CAMEL_FOLDER_SUMMARY(local_folder->summary), i);
+
+ array->pdata[i] = g_strdup(info->uid);
+ }
+
+ return array;
+}
+
+GPtrArray *
+local_get_summary(CamelFolder *folder)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+
+ return CAMEL_FOLDER_SUMMARY(local_folder->summary)->messages;
+}
+
+/* get a single message info, by uid */
+static const CamelMessageInfo *
+local_get_message_info(CamelFolder *folder, const char *uid)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+
+ return camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uid);
+}
+
+static GPtrArray *
+local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+
+ if (local_folder->search == NULL) {
+ local_folder->search = camel_folder_search_new();
+ }
+
+ camel_folder_search_set_folder(local_folder->search, folder);
+ if (local_folder->summary) {
+ /* FIXME: dont access summary array directly? */
+ camel_folder_search_set_summary(local_folder->search,
+ CAMEL_FOLDER_SUMMARY(local_folder->summary)->messages);
+ }
+
+ camel_folder_search_set_body_index(local_folder->search, local_folder->index);
+
+ return camel_folder_search_execute_expression(local_folder->search, expression, ex);
+}
+
+static void
+local_search_free(CamelFolder *folder, GPtrArray * result)
+{
+ CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
+
+ camel_folder_search_free_result(local_folder->search, result);
+}
+
+static guint32
+local_get_message_flags(CamelFolder *folder, const char *uid)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_val_if_fail(info != NULL, 0);
+
+ return info->flags;
+}
+
+static void
+local_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_if_fail(info != NULL);
+
+ info->flags = (info->flags & ~flags) | (set & flags) | CAMEL_MESSAGE_FOLDER_FLAGGED;
+ camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary));
+
+ camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
+}
+
+static gboolean
+local_get_message_user_flag(CamelFolder *folder, const char *uid, const char *name)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_val_if_fail(info != NULL, FALSE);
+
+ return camel_flag_get(&info->user_flags, name);
+}
+
+static void
+local_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_if_fail(info != NULL);
+
+ camel_flag_set(&info->user_flags, name, value);
+ info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
+ camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary));
+ camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
+}
+
+static const char *
+local_get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_val_if_fail(info != NULL, FALSE);
+
+ return camel_tag_get(&info->user_tags, name);
+}
+
+static void
+local_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
+{
+ CamelMessageInfo *info;
+ CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder);
+
+ info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid);
+ g_return_if_fail(info != NULL);
+
+ camel_tag_set(&info->user_tags, name, value);
+ info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
+ camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary));
+ camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid);
+}
+
+
diff --git a/camel/providers/local/camel-local-folder.h b/camel/providers/local/camel-local-folder.h
new file mode 100644
index 0000000000..96e4b23187
--- /dev/null
+++ b/camel/providers/local/camel-local-folder.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Author: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 1999 Helix Code (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_LOCAL_FOLDER_H
+#define CAMEL_LOCAL_FOLDER_H 1
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-folder.h>
+#include <camel/camel-folder-search.h>
+#include <libibex/ibex.h>
+#include "camel-local-summary.h"
+
+/* #include "camel-store.h" */
+
+#define CAMEL_LOCAL_FOLDER_TYPE (camel_local_folder_get_type ())
+#define CAMEL_LOCAL_FOLDER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_LOCAL_FOLDER_TYPE, CamelLocalFolder))
+#define CAMEL_LOCAL_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_LOCAL_FOLDER_TYPE, CamelLocalFolderClass))
+#define IS_CAMEL_LOCAL_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_LOCAL_FOLDER_TYPE))
+
+typedef struct {
+ CamelFolder parent_object;
+
+ guint32 flags; /* open mode flags */
+
+ int locked; /* lock counter */
+
+ char *base_path; /* base path of the local folder */
+ char *folder_path; /* the path to the folder itself */
+ char *summary_path; /* where the summary lives */
+ char *index_path; /* where the index file lives */
+
+ ibex *index; /* index for this folder */
+ CamelLocalSummary *summary;
+ CamelFolderSearch *search; /* used to run searches, we just use the real thing (tm) */
+ CamelFolderChangeInfo *changes; /* used to store changes to the folder during processing */
+} CamelLocalFolder;
+
+typedef struct {
+ CamelFolderClass parent_class;
+
+ /* Virtual methods */
+
+ /* summary factory, only used at init */
+ CamelLocalSummary *(*create_summary)(const char *path, const char *folder, ibex *index);
+
+ /* Lock the folder for my operations */
+ int (*lock)(CamelLocalFolder *);
+
+ /* Unlock the folder for my operations */
+ int (*unlock)(CamelLocalFolder *);
+} CamelLocalFolderClass;
+
+
+/* public methods */
+/* flags are taken from CAMEL_STORE_FOLDER_* flags */
+CamelLocalFolder *camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store,
+ const char *full_name, guint32 flags, CamelException *ex);
+
+/* Standard Camel function */
+CamelType camel_local_folder_get_type(void);
+
+/* Lock the folder for internal use. May be called repeatedly */
+/* UNIMPLEMENTED */
+int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex);
+int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_LOCAL_FOLDER_H */
diff --git a/camel/providers/local/camel-local-provider.c b/camel/providers/local/camel-local-provider.c
new file mode 100644
index 0000000000..f0beec0065
--- /dev/null
+++ b/camel/providers/local/camel-local-provider.c
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 2000 HelixCode (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-provider.h"
+#include "camel-session.h"
+#include "camel-url.h"
+
+#include "camel-mh-store.h"
+#include "camel-mbox-store.h"
+
+static CamelProvider mh_provider = {
+ "mh",
+ N_("UNIX MH-format mail directories (CamelLocal version)"),
+ N_("For storing local mail in MH-like mail directories"),
+ "mail",
+ CAMEL_PROVIDER_IS_STORAGE,
+ CAMEL_URL_NEED_PATH,
+ {0, 0},
+ NULL
+};
+
+static CamelProvider mbox_provider = {
+ "mbox",
+ N_("UNIX mbox-format mail files (CamelLocal version)"),
+ N_("For storing local mai in standard mbox format"),
+ "mail",
+ CAMEL_PROVIDER_IS_SOURCE | CAMEL_PROVIDER_IS_STORAGE,
+ CAMEL_URL_NEED_PATH,
+ { 0, 0 },
+ NULL
+};
+
+void camel_provider_module_init(CamelSession * session)
+{
+ printf("Initialising local providers\n");
+
+ mh_provider.object_types[CAMEL_PROVIDER_STORE] = camel_mh_store_get_type();
+ mh_provider.service_cache = g_hash_table_new(camel_url_hash, camel_url_equal);
+ camel_session_register_provider(session, &mh_provider);
+
+ mbox_provider.object_types[CAMEL_PROVIDER_STORE] = camel_mbox_store_get_type();
+ mbox_provider.service_cache = g_hash_table_new (camel_url_hash, camel_url_equal);
+ camel_session_register_provider (session, &mbox_provider);
+}
diff --git a/camel/providers/local/camel-local-store.c b/camel/providers/local/camel-local-store.c
new file mode 100644
index 0000000000..c07a1d2c3a
--- /dev/null
+++ b/camel/providers/local/camel-local-store.c
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 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
+ * 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 <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "camel-local-store.h"
+#include "camel-exception.h"
+#include "camel-url.h"
+
+/* Returns the class for a CamelLocalStore */
+#define CLOCALS_CLASS(so) CAMEL_LOCAL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static char *get_name(CamelService *service, gboolean brief);
+static void rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex);
+static char *get_folder_name(CamelStore *store, const char *folder_name, CamelException *ex);
+static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top,
+ gboolean fast, gboolean recursive,
+ gboolean subscribed_only,
+ CamelException *ex);
+static void delete_folder(CamelStore *store, const char *folder_name, CamelException *ex);
+static void rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex);
+
+static void
+camel_local_store_class_init (CamelLocalStoreClass *camel_local_store_class)
+{
+ CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_local_store_class);
+ CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_local_store_class);
+
+ /* virtual method overload */
+ camel_service_class->get_name = get_name;
+ camel_store_class->get_folder_name = get_folder_name;
+ camel_store_class->get_folder_info = get_folder_info;
+ camel_store_class->free_folder_info = camel_store_free_folder_info_full;
+
+ camel_store_class->delete_folder = delete_folder;
+ camel_store_class->rename_folder = rename_folder;
+}
+
+static void
+camel_local_store_init (gpointer object, gpointer klass)
+{
+ CamelStore *store = CAMEL_STORE (object);
+
+ /* local names are filenames, so they are case-sensitive. */
+ store->folders = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+CamelType
+camel_local_store_get_type (void)
+{
+ static CamelType camel_local_store_type = CAMEL_INVALID_TYPE;
+
+ if (camel_local_store_type == CAMEL_INVALID_TYPE) {
+ camel_local_store_type = camel_type_register (CAMEL_STORE_TYPE, "CamelLocalStore",
+ sizeof (CamelLocalStore),
+ sizeof (CamelLocalStoreClass),
+ (CamelObjectClassInitFunc) camel_local_store_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_local_store_init,
+ NULL);
+ }
+
+ return camel_local_store_type;
+}
+
+const char *
+camel_local_store_get_toplevel_dir (CamelLocalStore *store)
+{
+ CamelURL *url = CAMEL_SERVICE (store)->url;
+
+ g_assert (url != NULL);
+ return url->path;
+}
+
+static char *
+get_folder_name (CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ /* For now, we don't allow hieararchy. FIXME. */
+ if (strchr (folder_name + 1, '/')) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("Local folders may not be nested."));
+ return NULL;
+ }
+
+ return *folder_name == '/' ? g_strdup (folder_name) :
+ g_strdup_printf ("/%s", folder_name);
+}
+
+static char *
+get_name (CamelService *service, gboolean brief)
+{
+ if (brief)
+ return g_strdup (service->url->path);
+ else
+ return g_strdup_printf (_("Local mail file %s"), service->url->path);
+}
+
+static CamelFolderInfo *
+get_folder_info (CamelStore *store, const char *top,
+ gboolean fast, gboolean recursive,
+ gboolean subscribed_only,
+ CamelException *ex)
+{
+ /* FIXME: This is broken, but it corresponds to what was
+ * there before.
+ */
+ return NULL;
+}
+
+static int xrename(const char *oldp, const char *newp, const char *prefix, const char *suffix, CamelException *ex)
+{
+ struct stat st;
+ char *old = g_strconcat(prefix, oldp, suffix, 0);
+ char *new = g_strconcat(prefix, newp, suffix, 0);
+ int ret = -1;
+
+ printf("renaming %s%s to %s%s\n", oldp, suffix, newp, suffix);
+
+ /* FIXME: this has races ... */
+ if (!(stat(new, &st) == -1 && errno==ENOENT)) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not rename folder %s to %s: destination exists"),
+ old, new);
+ } else if (rename(old, new) == 0 || errno==ENOENT) {
+ ret = 0;
+ } else if (stat(old, &st) == -1 && errno==ENOENT && stat(new, &st) == 0) {
+ /* for nfs, check if the rename worked anyway ... */
+ ret = 0;
+ }
+
+ g_free(old);
+ g_free(new);
+ return ret;
+}
+
+/* default implementation, rename all */
+static void
+rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex)
+{
+ char *path = CAMEL_SERVICE (store)->url->path;
+
+ /* try to rollback failures, has obvious races */
+ if (xrename(old, new, path, ".ibex", ex)) {
+ return;
+ }
+ if (xrename(old, new, path, ".ev-summary", ex)) {
+ xrename(new, old, path, ".ibex", ex);
+ return;
+ }
+ if (xrename(old, new, path, "", ex)) {
+ xrename(new, old, path, ".ev-summary", ex);
+ xrename(new, old, path, ".ibex", ex);
+ }
+}
+
+/* default implementation, only delete metadata */
+static void
+delete_folder(CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ char *name;
+ char *str;
+
+ /* remove metadata only */
+ name = g_strdup_printf("%s%s", CAMEL_SERVICE(store)->url->path, folder_name);
+ str = g_strdup_printf("%s.ev-summary", name);
+ if (unlink(str) == -1 && errno != ENOENT) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not delete folder summary file `%s': %s"),
+ str, strerror(errno));
+ g_free(str);
+ return;
+ }
+ g_free(str);
+ str = g_strdup_printf("%s.ibex", name);
+ if (unlink(str) == -1 && errno != ENOENT) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not delete folder index file `%s': %s"),
+ str, strerror(errno));
+ g_free(str);
+ return;
+ }
+ g_free(str);
+ g_free(name);
+}
diff --git a/camel/providers/local/camel-local-store.h b/camel/providers/local/camel-local-store.h
new file mode 100644
index 0000000000..06191844d1
--- /dev/null
+++ b/camel/providers/local/camel-local-store.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-mbox-store.h : class for an mbox store */
+
+/*
+ *
+ * Copyright (C) 2000 Helix Code, Inc. <bertrand@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_LOCAL_STORE_H
+#define CAMEL_LOCAL_STORE_H 1
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include "camel-store.h"
+
+#define CAMEL_LOCAL_STORE_TYPE (camel_local_store_get_type ())
+#define CAMEL_LOCAL_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_LOCAL_STORE_TYPE, CamelLocalStore))
+#define CAMEL_LOCAL_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_LOCAL_STORE_TYPE, CamelLocalStoreClass))
+#define IS_CAMEL_LOCAL_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_LOCAL_STORE_TYPE))
+
+
+typedef struct {
+ CamelStore parent_object;
+
+} CamelLocalStore;
+
+
+
+typedef struct {
+ CamelStoreClass parent_class;
+
+} CamelLocalStoreClass;
+
+
+/* public methods */
+
+/* Standard Camel function */
+CamelType camel_local_store_get_type (void);
+
+const gchar *camel_local_store_get_toplevel_dir (CamelLocalStore *store);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_LOCAL_STORE_H */
+
+
diff --git a/camel/providers/local/camel-local-summary.c b/camel/providers/local/camel-local-summary.c
new file mode 100644
index 0000000000..68c83404cc
--- /dev/null
+++ b/camel/providers/local/camel-local-summary.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 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 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 "camel-local-summary.h"
+#include <camel/camel-mime-message.h>
+
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define io(x)
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+#define CAMEL_LOCAL_SUMMARY_VERSION (0x100)
+
+struct _CamelLocalSummaryPrivate {
+};
+
+#define _PRIVATE(o) (((CamelLocalSummary *)(o))->priv)
+
+#if 0
+static int summary_header_load (CamelFolderSummary *, FILE *);
+static int summary_header_save (CamelFolderSummary *, FILE *);
+#endif
+
+static CamelMessageInfo * message_info_new (CamelFolderSummary *, struct _header_raw *);
+static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
+static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg);
+#if 0
+static CamelMessageInfo * message_info_load (CamelFolderSummary *, FILE *);
+static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo *);
+#endif
+/*static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);*/
+
+static int local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *mi);
+static char *local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *mi);
+
+static int local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+static int local_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+static CamelMessageInfo *local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);
+
+static void camel_local_summary_class_init (CamelLocalSummaryClass *klass);
+static void camel_local_summary_init (CamelLocalSummary *obj);
+static void camel_local_summary_finalise (CamelObject *obj);
+
+static CamelFolderSummaryClass *camel_local_summary_parent;
+
+CamelType
+camel_local_summary_get_type(void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register(camel_folder_summary_get_type(), "CamelLocalSummary",
+ sizeof (CamelLocalSummary),
+ sizeof (CamelLocalSummaryClass),
+ (CamelObjectClassInitFunc) camel_local_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_local_summary_init,
+ (CamelObjectFinalizeFunc) camel_local_summary_finalise);
+ }
+
+ return type;
+}
+
+static void
+camel_local_summary_class_init(CamelLocalSummaryClass *klass)
+{
+ CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *) klass;
+
+ camel_local_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_type_get_global_classfuncs(camel_folder_summary_get_type()));
+
+ /*sklass->summary_header_load = summary_header_load;
+ sklass->summary_header_save = summary_header_save;*/
+
+ sklass->message_info_new = message_info_new;
+ sklass->message_info_new_from_parser = message_info_new_from_parser;
+ sklass->message_info_new_from_message = message_info_new_from_message;
+
+ /*sklass->message_info_load = message_info_load;
+ sklass->message_info_save = message_info_save;*/
+ /*sklass->message_info_free = message_info_free;*/
+
+ klass->check = local_summary_check;
+ klass->sync = local_summary_sync;
+ klass->add = local_summary_add;
+
+ klass->encode_x_evolution = local_summary_encode_x_evolution;
+ klass->decode_x_evolution = local_summary_decode_x_evolution;
+}
+
+static void
+camel_local_summary_init(CamelLocalSummary *obj)
+{
+ struct _CamelLocalSummaryPrivate *p;
+ struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
+
+ p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
+
+ /* subclasses need to set the right instance data sizes */
+ s->message_info_size = sizeof(CamelMessageInfo);
+ s->content_info_size = sizeof(CamelMessageContentInfo);
+
+ /* and a unique file version */
+ s->version += CAMEL_LOCAL_SUMMARY_VERSION;
+}
+
+static void
+camel_local_summary_finalise(CamelObject *obj)
+{
+ CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(obj);
+
+ g_free(mbs->folder_path);
+}
+
+void
+camel_local_summary_construct(CamelLocalSummary *new, const char *filename, const char *local_name, ibex *index)
+{
+ camel_folder_summary_set_build_content(CAMEL_FOLDER_SUMMARY(new), TRUE);
+ camel_folder_summary_set_filename(CAMEL_FOLDER_SUMMARY(new), filename);
+ new->folder_path = g_strdup(local_name);
+ new->index = index;
+}
+
+/* load/check the summary */
+int
+camel_local_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex)
+{
+ if (forceindex || camel_folder_summary_load((CamelFolderSummary *)cls) == -1) {
+ camel_folder_summary_clear((CamelFolderSummary *)cls);
+ }
+ return camel_local_summary_check(cls, NULL, ex);
+}
+
+char *
+camel_local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *info)
+{
+ return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->encode_x_evolution(cls, info);
+}
+
+int
+camel_local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *info)
+{
+ return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->decode_x_evolution(cls, xev, info);
+}
+
+int
+camel_local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->check(cls, changeinfo, ex);
+}
+
+int
+camel_local_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->sync(cls, expunge, changeinfo, ex);
+}
+
+CamelMessageInfo *
+camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex)
+{
+ return ((CamelLocalSummaryClass *)(CAMEL_OBJECT_GET_CLASS(cls)))->add(cls, msg, info, ci, ex);
+}
+
+#if 0
+static int
+summary_header_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s);
+
+ if (((CamelFolderSummaryClass *)camel_local_summary_parent)->summary_header_load(s, in) == -1)
+ return -1;
+
+ return camel_folder_summary_decode_uint32(in, &mbs->folder_size);
+}
+
+static int
+summary_header_save(CamelFolderSummary *s, FILE *out)
+{
+ CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s);
+
+ if (((CamelFolderSummaryClass *)camel_local_summary_parent)->summary_header_save(s, out) == -1)
+ return -1;
+
+ return camel_folder_summary_encode_uint32(out, mbs->folder_size);
+}
+
+static int
+header_evolution_decode(const char *in, guint32 *uid, guint32 *flags)
+{
+ char *header;
+
+ if (in && (header = header_token_decode(in))) {
+ if (strlen (header) == strlen ("00000000-0000")
+ && sscanf (header, "%08x-%04x", uid, flags) == 2) {
+ g_free(header);
+ return *uid;
+ }
+ g_free(header);
+ }
+
+ return -1;
+}
+
+static char *
+header_evolution_encode(guint32 uid, guint32 flags)
+{
+ return g_strdup_printf("%08x-%04x", uid, flags & 0xffff);
+}
+#endif
+
+static int
+local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ /* FIXME: sync index here */
+ return 0;
+}
+
+static int
+local_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ return 0;
+}
+
+static CamelMessageInfo *
+local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *ci, CamelException *ex)
+{
+ CamelMessageInfo *mi;
+ CamelFolderSummary *s = (CamelFolderSummary *)cls;
+ char *xev;
+
+ d(printf("Adding message to summary\n"));
+
+ mi = camel_folder_summary_add_from_message((CamelFolderSummary *)cls, msg);
+ if (mi) {
+ d(printf("Added, uid = %s\n", mi->uid));
+ if (info) {
+ CamelTag *tag = info->user_tags;
+ CamelFlag *flag = info->user_flags;
+
+ while (flag) {
+ camel_flag_set(&mi->user_flags, flag->name, TRUE);
+ flag = flag->next;
+ }
+
+ while (tag) {
+ camel_tag_set(&mi->user_tags, tag->name, tag->value);
+ tag = tag->next;
+ }
+
+ mi->flags = mi->flags | (info->flags & 0xffff);
+ }
+ xev = camel_local_summary_encode_x_evolution(cls, mi);
+ camel_medium_set_header((CamelMedium *)msg, "X-Evolution", xev);
+ g_free(xev);
+ camel_folder_change_info_add_uid(ci, mi->uid);
+ } else {
+ d(printf("Failed!\n"));
+ camel_exception_set(ex, 1, "Unable to add message to summary: unknown reason");
+ }
+ return mi;
+}
+
+static char *
+local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *mi)
+{
+ GString *out = g_string_new("");
+ struct _header_param *params = NULL;
+ GString *val = g_string_new("");
+ CamelFlag *flag = mi->user_flags;
+ CamelTag *tag = mi->user_tags;
+ char *ret;
+ guint32 uid;
+
+ /* FIXME: work out what to do with uid's that aren't stored here? */
+ /* FIXME: perhaps make that a mbox folder only issue?? */
+ if (sscanf(mi->uid, "%u", &uid) == 1) {
+ g_string_sprintf(out, "%08x-%04x", uid, mi->flags & 0xffff);
+ } else {
+ g_string_sprintf(out, "%s-%04x", mi->uid, mi->flags & 0xffff);
+ }
+
+ if (flag || tag) {
+ g_string_append(out, "; ");
+ val = g_string_new("");
+
+ if (flag) {
+ while (flag) {
+ g_string_append(val, flag->name);
+ if (flag->next)
+ g_string_append_c(out, ',');
+ flag = flag->next;
+ }
+ header_set_param(&params, "flags", val->str);
+ g_string_truncate(val, 0);
+ }
+ if (tag) {
+ while (tag) {
+ g_string_append(val, tag->name);
+ g_string_append_c(val, '=');
+ g_string_append(val, tag->value);
+ if (tag->next)
+ g_string_append_c(out, ',');
+ tag = tag->next;
+ }
+ header_set_param(&params, "tags", val->str);
+ }
+ g_string_free(val, TRUE);
+ header_param_list_format_append(out, params);
+ header_param_list_free(params);
+ }
+ ret = out->str;
+ g_string_free(out, FALSE);
+ return ret;
+}
+
+static int
+local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *mi)
+{
+ struct _header_param *params, *scan;
+ guint32 uid, flags;
+ char *header;
+ int i;
+
+ /* check for uid/flags */
+ header = header_token_decode(xev);
+ if (header && strlen(header) == strlen("00000000-0000")
+ && sscanf(header, "%08x-%04x", &uid, &flags) == 2) {
+ char uidstr[20];
+ sprintf(uidstr, "%u", uid);
+ g_free(mi->uid);
+ mi->uid = g_strdup(uidstr);
+ mi->flags = flags;
+ } else {
+ g_free(header);
+ return -1;
+ }
+ g_free(header);
+
+ /* check for additional data */
+ header = strchr(xev, ';');
+ if (header) {
+ params = header_param_list_decode(header+1);
+ scan = params;
+ while (scan) {
+ if (!strcasecmp(scan->name, "flags")) {
+ char **flagv = g_strsplit(scan->value, ",", 1000);
+
+ for (i=0;flagv[i];i++) {
+ camel_flag_set(&mi->user_flags, flagv[i], TRUE);
+ }
+ g_strfreev(flagv);
+ } else if (!strcasecmp(scan->name, "tags")) {
+ char **tagv = g_strsplit(scan->value, ",", 10000);
+ char *val;
+
+ for (i=0;tagv[i];i++) {
+ val = strchr(tagv[i], '=');
+ if (val) {
+ *val++ = 0;
+ camel_tag_set(&mi->user_tags, tagv[i], val);
+ val[-1]='=';
+ }
+ }
+ g_strfreev(tagv);
+ }
+ }
+ header_param_list_free(params);
+ }
+ return 0;
+}
+
+static CamelMessageInfo *
+message_info_new(CamelFolderSummary *s, struct _header_raw *h)
+{
+ CamelMessageInfo *mi;
+ CamelLocalSummary *cls = (CamelLocalSummary *)s;
+
+ mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_new(s, h);
+ if (mi) {
+ const char *xev;
+ int doindex = FALSE;
+
+ xev = header_raw_find(&h, "X-Evolution", NULL);
+ if (xev==NULL || camel_local_summary_decode_x_evolution(cls, xev, mi) == -1) {
+ /* to indicate it has no xev header */
+ mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV;
+ mi->uid = camel_folder_summary_next_uid_string(s);
+
+ /* shortcut, no need to look it up in the index library */
+ doindex = TRUE;
+ }
+
+ if (cls->index
+ && (doindex
+ || cls->index_force
+ || !ibex_contains_name(cls->index, mi->uid))) {
+ d(printf("Am indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, cls->index);
+ } else {
+ d(printf("Not indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, NULL);
+ }
+ }
+
+ return mi;
+}
+
+static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg)
+{
+ CamelMessageInfo *mi;
+ CamelLocalSummary *cls = (CamelLocalSummary *)s;
+
+ mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_new_from_message(s, msg);
+#if 0
+ if (mi) {
+
+ if (mi->uid == NULL) {
+ d(printf("no uid assigned yet, assigning one\n"));
+ mi->uid = camel_folder_summary_next_uid_string(s);
+ mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV;
+ }
+
+ if (cls->index
+ && (cls->index_force
+ || !ibex_contains_name(cls->index, mi->uid))) {
+ d(printf("Am indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, cls->index);
+ } else {
+ d(printf("Not indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, NULL);
+ }
+ }
+#endif
+ return mi;
+}
+
+static CamelMessageInfo *
+message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
+{
+ CamelMessageInfo *mi;
+ CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s);
+
+ mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_new_from_parser(s, mp);
+ if (mi) {
+#if 0
+ /* do we want to index this message as we add it, as well? */
+ if (mbs->index
+ && (mbs->index_force
+ || !ibex_contains_name(mbs->index, mi->uid))) {
+ d(printf("Am indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, mbs->index);
+ } else {
+ d(printf("Not indexing message %s\n", mi->uid));
+ camel_folder_summary_set_index(s, NULL);
+ }
+#endif
+ }
+
+ return mi;
+}
+
+#if 0
+static CamelMessageInfo *
+message_info_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelMessageInfo *mi;
+
+ io(printf("loading local message info\n"));
+
+ mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_load(s, in);
+ if (mi) {
+ guint32 position;
+ CamelLocalMessageInfo *mbi = (CamelLocalMessageInfo *)mi;
+
+ camel_folder_summary_decode_uint32(in, &position);
+ mbi->frompos = position;
+ }
+
+ return mi;
+}
+
+static int
+message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
+{
+ CamelLocalMessageInfo *mbi = (CamelLocalMessageInfo *)mi;
+
+ io(printf("saving local message info\n"));
+
+ ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_save(s, out, mi);
+
+ return camel_folder_summary_encode_uint32(out, mbi->frompos);
+}
+#endif
diff --git a/camel/providers/local/camel-local-summary.h b/camel/providers/local/camel-local-summary.h
new file mode 100644
index 0000000000..f1816e06e5
--- /dev/null
+++ b/camel/providers/local/camel-local-summary.h
@@ -0,0 +1,81 @@
+/*
+ * 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 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_LOCAL_SUMMARY_H
+#define _CAMEL_LOCAL_SUMMARY_H
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-exception.h>
+#include <libibex/ibex.h>
+
+#define CAMEL_LOCAL_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_local_summary_get_type (), CamelLocalSummary)
+#define CAMEL_LOCAL_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_local_summary_get_type (), CamelLocalSummaryClass)
+#define IS_CAMEL_LOCAL_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_local_summary_get_type ())
+
+typedef struct _CamelLocalSummary CamelLocalSummary;
+typedef struct _CamelLocalSummaryClass CamelLocalSummaryClass;
+
+/* extra summary flags */
+enum {
+ CAMEL_MESSAGE_FOLDER_NOXEV = 1<<17,
+};
+
+struct _CamelLocalSummary {
+ CamelFolderSummary parent;
+
+ struct _CamelLocalSummaryPrivate *priv;
+
+ char *folder_path; /* name of matching folder */
+
+ ibex *index;
+ int index_force; /* do we force index during creation? */
+};
+
+struct _CamelLocalSummaryClass {
+ CamelFolderSummaryClass parent_class;
+
+ int (*check)(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+ int (*sync)(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+ CamelMessageInfo *(*add)(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);
+
+ char *(*encode_x_evolution)(CamelLocalSummary *cls, const CamelMessageInfo *info);
+ int (*decode_x_evolution)(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *info);
+};
+
+guint camel_local_summary_get_type (void);
+void camel_local_summary_construct (CamelLocalSummary *new, const char *filename, const char *local_name, ibex *index);
+
+/* load/check the summary */
+int camel_local_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex);
+/* check for new/removed messages */
+int camel_local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *, CamelException *ex);
+/* perform a folder sync or expunge, if needed */
+int camel_local_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *, CamelException *ex);
+/* add a new message to the summary */
+CamelMessageInfo *camel_local_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);
+
+/* generate an X-Evolution header line */
+char *camel_local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *info);
+int camel_local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *info);
+
+#endif /* ! _CAMEL_LOCAL_SUMMARY_H */
+
diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c
new file mode 100644
index 0000000000..d12b6431de
--- /dev/null
+++ b/camel/providers/local/camel-mbox-folder.c
@@ -0,0 +1,322 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * 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
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "camel-mbox-folder.h"
+#include "camel-mbox-store.h"
+#include "string-utils.h"
+#include "camel-stream-fs.h"
+#include "camel-mbox-summary.h"
+#include "camel-data-wrapper.h"
+#include "camel-mime-message.h"
+#include "camel-stream-filter.h"
+#include "camel-mime-filter-from.h"
+#include "camel-exception.h"
+
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+static CamelLocalFolderClass *parent_class = NULL;
+
+/* Returns the class for a CamelMboxFolder */
+#define CMBOXF_CLASS(so) CAMEL_MBOX_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+
+static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex);
+static CamelMimeMessage *mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex);
+static CamelLocalSummary *mbox_create_summary(const char *path, const char *folder, ibex *index);
+
+static void mbox_finalise(CamelObject * object);
+
+static void
+camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class)
+{
+ CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_mbox_folder_class);
+ CamelLocalFolderClass *lclass = (CamelLocalFolderClass *)camel_mbox_folder_class;
+
+ parent_class = (CamelLocalFolderClass *)camel_type_get_global_classfuncs(camel_local_folder_get_type());
+
+ /* virtual method definition */
+
+ /* virtual method overload */
+ camel_folder_class->append_message = mbox_append_message;
+ camel_folder_class->get_message = mbox_get_message;
+
+ lclass->create_summary = mbox_create_summary;
+}
+
+static void
+mbox_init(gpointer object, gpointer klass)
+{
+ /*CamelFolder *folder = object;
+ CamelMboxFolder *mbox_folder = object;*/
+}
+
+static void
+mbox_finalise(CamelObject * object)
+{
+ /*CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(object);*/
+}
+
+CamelType camel_mbox_folder_get_type(void)
+{
+ static CamelType camel_mbox_folder_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mbox_folder_type == CAMEL_INVALID_TYPE) {
+ camel_mbox_folder_type = camel_type_register(CAMEL_LOCAL_FOLDER_TYPE, "CamelMboxFolder",
+ sizeof(CamelMboxFolder),
+ sizeof(CamelMboxFolderClass),
+ (CamelObjectClassInitFunc) camel_mbox_folder_class_init,
+ NULL,
+ (CamelObjectInitFunc) mbox_init,
+ (CamelObjectFinalizeFunc) mbox_finalise);
+ }
+
+ return camel_mbox_folder_type;
+}
+
+CamelFolder *
+camel_mbox_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex)
+{
+ CamelFolder *folder;
+
+ d(printf("Creating mbox folder: %s\n", full_name));
+
+ folder = (CamelFolder *)camel_object_new(CAMEL_MBOX_FOLDER_TYPE);
+ folder = (CamelFolder *)camel_local_folder_construct((CamelLocalFolder *)folder,
+ parent_store, full_name, flags, ex);
+
+ return folder;
+}
+
+static CamelLocalSummary *mbox_create_summary(const char *path, const char *folder, ibex *index)
+{
+ return (CamelLocalSummary *)camel_mbox_summary_new(path, folder, index);
+}
+
+static void
+mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex)
+{
+ CamelLocalFolder *lf = (CamelLocalFolder *)folder;
+ CamelStream *output_stream = NULL, *filter_stream = NULL;
+ CamelMimeFilter *filter_from = NULL;
+ CamelMboxSummary *mbs = (CamelMboxSummary *)lf->summary;
+ CamelMessageInfo *mi;
+ char *fromline = NULL;
+ int fd;
+ struct stat st;
+
+ /* FIXME: Locking */
+
+ d(printf("Appending message\n"));
+
+ /* first, check the summary is correct (updates folder_size too) */
+ camel_local_summary_check(lf->summary, lf->changes, ex);
+ if (camel_exception_is_set(ex))
+ return;
+
+ /* add it to the summary/assign the uid, etc */
+ mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex);
+ if (camel_exception_is_set(ex)) {
+ return;
+ }
+
+ d(printf("Appending message: uid is %s\n", mi->uid));
+
+ output_stream = camel_stream_fs_new_with_name(lf->folder_path, O_WRONLY|O_APPEND, 0600);
+ if (output_stream == NULL) {
+ camel_exception_setv(ex, 1, _("Cannot open mailbox: %s: %s\n"), lf->folder_path, strerror(errno));
+ return;
+ }
+
+ /* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */
+ fromline = camel_mbox_summary_build_from(((CamelMimePart *)message)->headers);
+ if (camel_stream_printf(output_stream, mbs->folder_size==0?"%s":"\n%s", fromline) == -1)
+ goto fail_write;
+
+ /* and write the content to the filtering stream, that translated '\nFrom' into '\n>From' */
+ filter_stream = (CamelStream *) camel_stream_filter_new_with_stream(output_stream);
+ filter_from = (CamelMimeFilter *) camel_mime_filter_from_new();
+ camel_stream_filter_add((CamelStreamFilter *) filter_stream, filter_from);
+ if (camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, filter_stream) == -1)
+ goto fail_write;
+
+ if (camel_stream_close(filter_stream) == -1)
+ goto fail_write;
+
+ /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */
+ /* the stat really shouldn't fail, we just wrote to it */
+ if (stat(lf->folder_path, &st) == 0) {
+ mbs->folder_size = st.st_size;
+ ((CamelFolderSummary *)mbs)->time = st.st_mtime;
+ }
+
+ /* filter stream ref's the output stream itself, so we need to unref it too */
+ camel_object_unref((CamelObject *)filter_from);
+ camel_object_unref((CamelObject *)filter_stream);
+ camel_object_unref((CamelObject *)output_stream);
+ g_free(fromline);
+
+ if (camel_folder_change_info_changed(lf->changes)) {
+ camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes);
+ camel_folder_change_info_clear(lf->changes);
+ }
+
+ return;
+
+fail_write:
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot append message to mbox file: %s: %s"), lf->folder_path, g_strerror(errno));
+
+ if (filter_stream)
+ camel_object_unref(CAMEL_OBJECT(filter_stream));
+
+ if (output_stream)
+ camel_object_unref(CAMEL_OBJECT(output_stream));
+
+ if (filter_from)
+ camel_object_unref(CAMEL_OBJECT(filter_from));
+
+ g_free(fromline);
+
+ /* reset the file to original size */
+ fd = open(lf->folder_path, O_WRONLY, 0600);
+
+ if (fd != -1) {
+ ftruncate(fd, mbs->folder_size);
+ close(fd);
+ }
+
+ /* and tell the summary its uptodate */
+ if (stat(lf->folder_path, &st) == 0) {
+ mbs->folder_size = st.st_size;
+ ((CamelFolderSummary *)mbs)->time = st.st_mtime;
+ }
+
+ /* cascade the changes through, anyway, if there are any outstanding */
+ if (camel_folder_change_info_changed(lf->changes)) {
+ camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes);
+ camel_folder_change_info_clear(lf->changes);
+ }
+}
+
+static CamelMimeMessage *
+mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex)
+{
+ CamelLocalFolder *lf = (CamelLocalFolder *)folder;
+ CamelMimeMessage *message;
+ CamelMboxMessageInfo *info;
+ CamelMimeParser *parser;
+ int fd;
+ int retried = FALSE;
+
+ d(printf("Getting message %s\n", uid));
+
+ /* FIXME: mbox locking */
+
+retry:
+ /* get the message summary info */
+ info = (CamelMboxMessageInfo *) camel_folder_summary_uid((CamelFolderSummary *)lf->summary, uid);
+
+ if (info == NULL) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message: %s\n %s"), uid, _("No such message"));
+ return NULL;
+ }
+
+ /* if this has no content, its an error in the library */
+ g_assert(info->info.content);
+ g_assert(info->frompos != -1);
+
+ /* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache
+ the whole message in memory if the stream is non-seekable (which it is when built from a parser
+ with no stream). This means we dont have to lock the mbox for the life of the message. */
+
+ fd = open(lf->folder_path, O_RDONLY);
+ if (fd == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path,
+ strerror(errno));
+ return NULL;
+ }
+
+ /* we use a parser to verify the message is correct, and in the correct position */
+ parser = camel_mime_parser_new();
+ camel_mime_parser_init_with_fd(parser, fd);
+ camel_mime_parser_scan_from(parser, TRUE);
+
+ camel_mime_parser_seek(parser, info->frompos, SEEK_SET);
+ if (camel_mime_parser_step(parser, NULL, NULL) != HSCAN_FROM
+ || camel_mime_parser_tell_start_from(parser) != info->frompos) {
+
+ g_warning("Summary doesn't match the folder contents! eek!\n"
+ " expecting offset %ld got %ld", (long int)info->frompos,
+ (long int)camel_mime_parser_tell_start_from(parser));
+
+ camel_object_unref((CamelObject *)parser);
+
+ if (!retried) {
+ retried = TRUE;
+ camel_local_summary_check(lf->summary, lf->changes, ex);
+ if (!camel_exception_is_set(ex))
+ goto retry;
+ }
+
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path,
+ _("The folder appears to be irrecoverably corrupted."));
+
+ return NULL;
+ }
+
+ message = camel_mime_message_new();
+ if (camel_mime_part_construct_from_parser((CamelMimePart *)message, parser) == -1) {
+ g_warning("Construction failed");
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path,
+ _("Message construction failed: Corrupt mailbox?"));
+ camel_object_unref((CamelObject *)parser);
+ camel_object_unref((CamelObject *)message);
+ return NULL;
+ }
+
+ camel_object_unref((CamelObject *)parser);
+
+ /* use the opportunity to notify of changes (particularly if we had a rebuild) */
+ if (camel_folder_change_info_changed(lf->changes)) {
+ camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes);
+ camel_folder_change_info_clear(lf->changes);
+ }
+
+ return message;
+}
diff --git a/camel/providers/local/camel-mbox-folder.h b/camel/providers/local/camel-mbox-folder.h
new file mode 100644
index 0000000000..573b5df177
--- /dev/null
+++ b/camel/providers/local/camel-mbox-folder.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 1999 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.
+ *
+ * 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_MBOX_FOLDER_H
+#define CAMEL_MBOX_FOLDER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include "camel-local-folder.h"
+#include "camel-mbox-summary.h"
+
+#define CAMEL_MBOX_FOLDER_TYPE (camel_mbox_folder_get_type ())
+#define CAMEL_MBOX_FOLDER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MBOX_FOLDER_TYPE, CamelMboxFolder))
+#define CAMEL_MBOX_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MBOX_FOLDER_TYPE, CamelMboxFolderClass))
+#define IS_CAMEL_MBOX_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_MBOX_FOLDER_TYPE))
+
+typedef struct {
+ CamelLocalFolder parent_object;
+
+} CamelMboxFolder;
+
+typedef struct {
+ CamelLocalFolderClass parent_class;
+
+ /* Virtual methods */
+
+} CamelMboxFolderClass;
+
+/* public methods */
+/* flags are taken from CAMEL_STORE_FOLDER_* flags */
+CamelFolder *camel_mbox_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex);
+
+/* Standard Camel function */
+CamelType camel_mbox_folder_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_MBOX_FOLDER_H */
diff --git a/camel/providers/local/camel-mbox-provider.c b/camel/providers/local/camel-mbox-provider.c
new file mode 100644
index 0000000000..bfce8b5ada
--- /dev/null
+++ b/camel/providers/local/camel-mbox-provider.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-mbox-provider.c: mbox provider registration code */
+
+/*
+ * Authors :
+ * Bertrand Guiheneuf <bertrand@helixcode.com>
+ *
+ * Copyright (C) 2000 HelixCode (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-mbox-store.h"
+#include "camel-provider.h"
+#include "camel-session.h"
+#include "camel-url.h"
+
+static CamelProvider mbox_provider = {
+ "mbox",
+ N_("UNIX mbox-format mail files"),
+
+ N_("For reading mail delivered by the local system, and for "
+ "storing mail on local disk."),
+
+ "mail",
+
+ CAMEL_PROVIDER_IS_SOURCE | CAMEL_PROVIDER_IS_STORAGE,
+
+ CAMEL_URL_NEED_PATH,
+
+ { 0, 0 },
+
+ NULL
+};
+
+void
+camel_provider_module_init (CamelSession *session)
+{
+ mbox_provider.object_types[CAMEL_PROVIDER_STORE] =
+ camel_mbox_store_get_type();
+
+ mbox_provider.service_cache = g_hash_table_new (camel_url_hash, camel_url_equal);
+
+ camel_session_register_provider (session, &mbox_provider);
+}
diff --git a/camel/providers/local/camel-mbox-store.c b/camel/providers/local/camel-mbox-store.c
new file mode 100644
index 0000000000..77a291637c
--- /dev/null
+++ b/camel/providers/local/camel-mbox-store.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 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
+ * 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 <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "camel-mbox-store.h"
+#include "camel-mbox-folder.h"
+#include "camel-exception.h"
+#include "camel-url.h"
+
+static CamelLocalStoreClass *parent_class = NULL;
+
+/* Returns the class for a CamelMboxStore */
+#define CMBOXS_CLASS(so) CAMEL_MBOX_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CMBOXF_CLASS(so) CAMEL_MBOX_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static CamelFolder *get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex);
+static void delete_folder(CamelStore *store, const char *folder_name, CamelException *ex);
+
+static void
+camel_mbox_store_class_init (CamelMboxStoreClass *camel_mbox_store_class)
+{
+ CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_mbox_store_class);
+
+ parent_class = (CamelLocalStoreClass *)camel_type_get_global_classfuncs(camel_folder_get_type());
+
+ /* virtual method overload */
+ camel_store_class->get_folder = get_folder;
+ camel_store_class->delete_folder = delete_folder;
+}
+
+static void
+camel_mbox_store_init (gpointer object, gpointer klass)
+{
+ CamelStore *store = CAMEL_STORE (object);
+
+ /* mbox names are filenames, so they are case-sensitive. */
+ store->folders = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+CamelType
+camel_mbox_store_get_type (void)
+{
+ static CamelType camel_mbox_store_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mbox_store_type == CAMEL_INVALID_TYPE) {
+ camel_mbox_store_type = camel_type_register (CAMEL_LOCAL_STORE_TYPE, "CamelMboxStore",
+ sizeof (CamelMboxStore),
+ sizeof (CamelMboxStoreClass),
+ (CamelObjectClassInitFunc) camel_mbox_store_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mbox_store_init,
+ NULL);
+ }
+
+ return camel_mbox_store_type;
+}
+
+static CamelFolder *
+get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
+{
+ char *name;
+ struct stat st;
+
+ name = g_strdup_printf("%s%s", CAMEL_SERVICE(store)->url->path, folder_name);
+
+ if (stat(name, &st) == -1) {
+ int fd;
+
+ if (errno != ENOENT) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not open file `%s':\n%s"),
+ name, g_strerror(errno));
+ g_free(name);
+ return NULL;
+ }
+ if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("Folder `%s' does not exist."),
+ folder_name);
+ g_free(name);
+ return NULL;
+ }
+
+ fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ if (fd == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not create file `%s':\n%s"),
+ name, g_strerror(errno));
+ g_free(name);
+ return NULL;
+ }
+ g_free(name);
+ close(fd);
+ } else if (!S_ISREG(st.st_mode)) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("`%s' is not a regular file."),
+ name);
+ g_free(name);
+ return NULL;
+ } else
+ g_free(name);
+
+ return camel_mbox_folder_new(store, folder_name, flags, ex);
+}
+
+static void
+delete_folder (CamelStore *store, const char *folder_name, CamelException *ex)
+{
+ char *name;
+ struct stat st;
+
+ name = g_strdup_printf ("%s%s", CAMEL_SERVICE (store)->url->path, folder_name);
+ if (stat (name, &st) == -1) {
+ if (errno == ENOENT) {
+ /* file doesn't exist - it's kinda like deleting it ;-) */
+ g_free (name);
+ return;
+ }
+
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not delete folder `%s':\n%s"),
+ folder_name, g_strerror (errno));
+ g_free (name);
+ return;
+ }
+
+ if (!S_ISREG (st.st_mode)) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("`%s' is not a regular file."), name);
+ g_free (name);
+ return;
+ }
+
+ if (st.st_size != 0) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_NON_EMPTY,
+ _("Folder `%s' is not empty. Not deleted."),
+ folder_name);
+ g_free (name);
+ return;
+ }
+
+ if (unlink(name) == -1 && errno != ENOENT) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not delete folder `%s':\n%s"),
+ name, g_strerror (errno));
+ g_free(name);
+ return;
+ }
+
+ g_free(name);
+
+ /* and remove metadata */
+ ((CamelStoreClass *)parent_class)->delete_folder(store, folder_name, ex);
+}
diff --git a/camel/providers/local/camel-mbox-store.h b/camel/providers/local/camel-mbox-store.h
new file mode 100644
index 0000000000..29e0e91041
--- /dev/null
+++ b/camel/providers/local/camel-mbox-store.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 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
+ * 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_MBOX_STORE_H
+#define CAMEL_MBOX_STORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include "camel-local-store.h"
+
+#define CAMEL_MBOX_STORE_TYPE (camel_mbox_store_get_type ())
+#define CAMEL_MBOX_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MBOX_STORE_TYPE, CamelMboxStore))
+#define CAMEL_MBOX_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MBOX_STORE_TYPE, CamelMboxStoreClass))
+#define IS_CAMEL_MBOX_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_MBOX_STORE_TYPE))
+
+typedef struct {
+ CamelLocalStore parent_object;
+
+} CamelMboxStore;
+
+typedef struct {
+ CamelLocalStoreClass parent_class;
+
+} CamelMboxStoreClass;
+
+/* public methods */
+
+/* Standard Camel function */
+CamelType camel_mbox_store_get_type (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_MBOX_STORE_H */
+
+
diff --git a/camel/providers/local/camel-mbox-summary.c b/camel/providers/local/camel-mbox-summary.c
new file mode 100644
index 0000000000..32bf411fc6
--- /dev/null
+++ b/camel/providers/local/camel-mbox-summary.c
@@ -0,0 +1,856 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 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 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 "camel-mbox-summary.h"
+#include <camel/camel-mime-message.h>
+
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define io(x)
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+#define CAMEL_MBOX_SUMMARY_VERSION (0x1000)
+
+struct _CamelMboxSummaryPrivate {
+};
+
+#define _PRIVATE(o) (((CamelMboxSummary *)(o))->priv)
+
+static int summary_header_load (CamelFolderSummary *, FILE *);
+static int summary_header_save (CamelFolderSummary *, FILE *);
+
+static CamelMessageInfo * message_info_new (CamelFolderSummary *, struct _header_raw *);
+static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *);
+static CamelMessageInfo * message_info_load (CamelFolderSummary *, FILE *);
+static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo *);
+/*static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);*/
+
+static int mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+static int mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+
+static void camel_mbox_summary_class_init (CamelMboxSummaryClass *klass);
+static void camel_mbox_summary_init (CamelMboxSummary *obj);
+static void camel_mbox_summary_finalise (CamelObject *obj);
+
+static CamelLocalSummaryClass *camel_mbox_summary_parent;
+
+CamelType
+camel_mbox_summary_get_type(void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register(camel_local_summary_get_type(), "CamelMboxSummary",
+ sizeof (CamelMboxSummary),
+ sizeof (CamelMboxSummaryClass),
+ (CamelObjectClassInitFunc) camel_mbox_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mbox_summary_init,
+ (CamelObjectFinalizeFunc) camel_mbox_summary_finalise);
+ }
+
+ return type;
+}
+
+static void
+camel_mbox_summary_class_init(CamelMboxSummaryClass *klass)
+{
+ CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *)klass;
+ CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)klass;
+
+ camel_mbox_summary_parent = (CamelLocalSummaryClass *)camel_type_get_global_classfuncs(camel_local_summary_get_type());
+
+ sklass->summary_header_load = summary_header_load;
+ sklass->summary_header_save = summary_header_save;
+
+ sklass->message_info_new = message_info_new;
+ sklass->message_info_new_from_parser = message_info_new_from_parser;
+ sklass->message_info_load = message_info_load;
+ sklass->message_info_save = message_info_save;
+ /*sklass->message_info_free = message_info_free;*/
+
+ lklass->check = mbox_summary_check;
+ lklass->sync = mbox_summary_sync;
+}
+
+static void
+camel_mbox_summary_init(CamelMboxSummary *obj)
+{
+ struct _CamelMboxSummaryPrivate *p;
+ struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
+
+ p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
+
+ /* subclasses need to set the right instance data sizes */
+ s->message_info_size = sizeof(CamelMboxMessageInfo);
+ s->content_info_size = sizeof(CamelMboxMessageContentInfo);
+
+ /* and a unique file version */
+ s->version += CAMEL_MBOX_SUMMARY_VERSION;
+}
+
+static void
+camel_mbox_summary_finalise(CamelObject *obj)
+{
+ /*CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(obj);*/
+}
+
+/**
+ * camel_mbox_summary_new:
+ *
+ * Create a new CamelMboxSummary object.
+ *
+ * Return value: A new CamelMboxSummary widget.
+ **/
+CamelMboxSummary *
+camel_mbox_summary_new(const char *filename, const char *mbox_name, ibex *index)
+{
+ CamelMboxSummary *new = (CamelMboxSummary *)camel_object_new(camel_mbox_summary_get_type());
+
+ camel_local_summary_construct((CamelLocalSummary *)new, filename, mbox_name, index);
+ return new;
+}
+
+static int
+summary_header_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(s);
+
+ if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->summary_header_load(s, in) == -1)
+ return -1;
+
+ return camel_folder_summary_decode_uint32(in, &mbs->folder_size);
+}
+
+static int
+summary_header_save(CamelFolderSummary *s, FILE *out)
+{
+ CamelMboxSummary *mbs = CAMEL_MBOX_SUMMARY(s);
+
+ if (((CamelFolderSummaryClass *)camel_mbox_summary_parent)->summary_header_save(s, out) == -1)
+ return -1;
+
+ return camel_folder_summary_encode_uint32(out, mbs->folder_size);
+}
+
+static int
+header_evolution_decode(const char *in, guint32 *uid, guint32 *flags)
+{
+ char *header;
+
+ if (in && (header = header_token_decode(in))) {
+ if (strlen (header) == strlen ("00000000-0000")
+ && sscanf (header, "%08x-%04x", uid, flags) == 2) {
+ g_free(header);
+ return *uid;
+ }
+ g_free(header);
+ }
+
+ return -1;
+}
+
+/* we still use our own version here, as we dont grok the flag stuff yet, during an expunge
+ anyway */
+static char *
+header_evolution_encode(guint32 uid, guint32 flags)
+{
+ return g_strdup_printf("%08x-%04x", uid, flags & 0xffff);
+}
+
+static CamelMessageInfo *
+message_info_new(CamelFolderSummary *s, struct _header_raw *h)
+{
+ CamelMessageInfo *mi;
+
+ mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_new(s, h);
+ if (mi) {
+ CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
+
+ mbi->frompos = -1;
+ }
+
+ return mi;
+}
+
+static CamelMessageInfo *
+message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
+{
+ CamelMessageInfo *mi;
+
+ mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_new_from_parser(s, mp);
+ if (mi) {
+ CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
+
+ mbi->frompos = camel_mime_parser_tell_start_from(mp);
+ }
+
+ return mi;
+}
+
+static CamelMessageInfo *
+message_info_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelMessageInfo *mi;
+
+ io(printf("loading mbox message info\n"));
+
+ mi = ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_load(s, in);
+ if (mi) {
+ CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
+
+ camel_folder_summary_decode_off_t(in, &mbi->frompos);
+ }
+
+ return mi;
+}
+
+static int
+message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
+{
+ CamelMboxMessageInfo *mbi = (CamelMboxMessageInfo *)mi;
+
+ io(printf("saving mbox message info\n"));
+
+ ((CamelFolderSummaryClass *)camel_mbox_summary_parent)->message_info_save(s, out, mi);
+
+ return camel_folder_summary_encode_off_t(out, mbi->frompos);
+}
+
+static int
+summary_rebuild(CamelMboxSummary *mbs, off_t offset, CamelException *ex)
+{
+ CamelLocalSummary *cls = (CamelLocalSummary *)mbs;
+ CamelFolderSummary *s = (CamelFolderSummary *)mbs;
+ CamelMimeParser *mp;
+ int fd;
+ int ok = 0;
+
+ fd = open(cls->folder_path, O_RDONLY);
+ if (fd == -1) {
+ printf("%s failed to open: %s", cls->folder_path, strerror(errno));
+ camel_exception_setv(ex, 1, _("Could not open folder: %s: summarising from position %ld: %s"),
+ cls->folder_path, offset, strerror(errno));
+ return -1;
+ }
+
+ mp = camel_mime_parser_new();
+ camel_mime_parser_init_with_fd(mp, fd);
+ camel_mime_parser_scan_from(mp, TRUE);
+ camel_mime_parser_seek(mp, offset, SEEK_SET);
+
+ if (offset > 0) {
+ if (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) {
+ if (camel_mime_parser_tell_start_from(mp) != offset) {
+ g_warning("The next message didn't start where I expected, building summary from start");
+ camel_mime_parser_drop_step(mp);
+ offset = 0;
+ camel_mime_parser_seek(mp, offset, SEEK_SET);
+ camel_folder_summary_clear(s);
+ } else {
+ camel_mime_parser_unstep(mp);
+ }
+ } else {
+ camel_object_unref(CAMEL_OBJECT(mp));
+ /* end of file - no content? */
+ return -1;
+ }
+ }
+
+ while (camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_summary_add_from_parser(s, mp);
+ if (info == NULL) {
+ camel_exception_setv(ex, 1, _("Fatal mail parser error near position %ld in folder %s"),
+ camel_mime_parser_tell(mp), cls->folder_path);
+ ok = -1;
+ break;
+ }
+
+ g_assert(camel_mime_parser_step(mp, NULL, NULL) == HSCAN_FROM_END);
+ }
+
+ camel_object_unref(CAMEL_OBJECT (mp));
+
+ /* update the file size/mtime in the summary */
+ if (ok != -1) {
+ struct stat st;
+
+ if (stat(cls->folder_path, &st) == 0) {
+ mbs->folder_size = st.st_size;
+ s->time = st.st_mtime;
+ }
+ }
+
+ return ok;
+}
+
+/* like summary_rebuild, but also do changeinfo stuff (if supplied) */
+static int
+summary_update(CamelMboxSummary *mbs, off_t offset, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ int ret, i, count;
+ CamelFolderSummary *s = (CamelFolderSummary *)mbs;
+ CamelLocalSummary *cls = (CamelLocalSummary *)mbs;
+
+ if (changeinfo) {
+ /* we use the diff function of the change_info to build the update list. */
+ for (i = 0; i < camel_folder_summary_count(s); i++) {
+ CamelMessageInfo *mi = camel_folder_summary_index(s, i);
+
+ camel_folder_change_info_add_source(changeinfo, mi->uid);
+ }
+ }
+
+ /* do the actual work */
+ cls->index_force = FALSE;
+ ret = summary_rebuild(mbs, offset, ex);
+
+ if (changeinfo) {
+ count = camel_folder_summary_count(s);
+ for (i = 0; i < count; i++) {
+ CamelMessageInfo *mi = camel_folder_summary_index(s, i);
+ camel_folder_change_info_add_update(changeinfo, mi->uid);
+ }
+ camel_folder_change_info_build_diff(changeinfo);
+ }
+
+ return ret;
+}
+
+static int
+mbox_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex)
+{
+ CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
+ CamelFolderSummary *s = (CamelFolderSummary *)cls;
+ struct stat st;
+ int ret = 0;
+
+ d(printf("Checking summary\n"));
+
+ /* check if the summary is up-to-date */
+ if (stat(cls->folder_path, &st) == -1) {
+ camel_folder_summary_clear(s);
+ camel_exception_setv(ex, 1, _("Cannot summarise folder: %s: %s"), cls->folder_path, strerror(errno));
+ return -1;
+ }
+
+ if (st.st_size == 0) {
+ /* empty? No need to scan at all */
+ d(printf("Empty mbox, clearing summary\n"));
+ camel_folder_summary_clear(s);
+ ret = 0;
+ } else if (s->messages->len == 0) {
+ /* if we are empty, then we rebuilt from scratch */
+ d(printf("Empty summary, rebuilding from start\n"));
+ ret = summary_update(mbs, 0, changes, ex);
+ } else {
+ /* is the summary uptodate? */
+ if (st.st_size != mbs->folder_size || st.st_mtime != s->time) {
+ if (mbs->folder_size < st.st_size) {
+ /* this will automatically rescan from 0 if there is a problem */
+ d(printf("folder grew, attempting to rebuild from %d\n", mbs->folder_size));
+ ret = summary_update(mbs, mbs->folder_size, changes, ex);
+ } else {
+ d(printf("folder shrank! rebuilding from start\n"));
+ camel_folder_summary_clear(s);
+ ret = summary_update(mbs, 0, changes, ex);
+ }
+ }
+ }
+
+ /* FIXME: move upstream? */
+
+ if (ret != -1) {
+ mbs->folder_size = st.st_size;
+ s->time = st.st_mtime;
+#if 0
+ /* this failing is not a fatal event */
+ if (camel_folder_summary_save(s) == -1)
+ g_warning("Could not save summary: %s", strerror(errno));
+ if (cls->index)
+ ibex_save(cls->index);
+#endif
+ }
+
+ return ret;
+}
+
+static int
+header_write(int fd, struct _header_raw *header, char *xevline)
+{
+ struct iovec iv[4];
+ int outlen = 0, len;
+
+ iv[1].iov_base = ":";
+ iv[1].iov_len = 1;
+ iv[3].iov_base = "\n";
+ iv[3].iov_len = 1;
+
+ while (header) {
+ if (strcasecmp(header->name, "X-Evolution")) {
+ 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, 4);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -1;
+ outlen += len;
+ }
+ header = header->next;
+ }
+
+ iv[0].iov_base = "X-Evolution: ";
+ iv[0].iov_len = strlen(iv[0].iov_base);
+ iv[1].iov_base = xevline;
+ iv[1].iov_len = strlen(xevline);
+ iv[2].iov_base = "\n\n";
+ iv[2].iov_len = 2;
+
+ do {
+ len = writev(fd, iv, 3);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -1;
+
+ outlen += 1;
+
+ d(printf("Wrote %d bytes of headers\n", outlen));
+
+ return outlen;
+}
+
+static int
+copy_block(int fromfd, int tofd, off_t start, size_t bytes)
+{
+ char buffer[4096];
+ int written = 0;
+
+ d(printf("writing %d bytes ... \n", bytes));
+
+ if (lseek(fromfd, start, SEEK_SET) != start)
+ return -1;
+
+ while (bytes > 0) {
+ int toread, towrite;
+
+ toread = bytes;
+ if (bytes > 4096)
+ toread = 4096;
+ else
+ toread = bytes;
+ do {
+ towrite = read(fromfd, buffer, toread);
+ } while (towrite == -1 && errno == EINTR);
+
+ if (towrite == -1)
+ return -1;
+
+ /* check for 'end of file' */
+ if (towrite == 0) {
+ d(printf("end of file?\n"));
+ break;
+ }
+
+ do {
+ toread = write(tofd, buffer, towrite);
+ } while (toread == -1 && errno == EINTR);
+
+ if (toread == -1)
+ return -1;
+
+ written += toread;
+ bytes -= toread;
+ }
+
+ d(printf("written %d bytes\n", written));
+
+ return written;
+}
+
+static char *tz_months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static char *tz_days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+/* tries to build a From line, based on message headers */
+char *
+camel_mbox_summary_build_from(struct _header_raw *header)
+{
+ GString *out = g_string_new("From ");
+ char *ret;
+ const char *tmp;
+ time_t thetime;
+ int offset;
+ struct tm tm;
+
+ tmp = header_raw_find(&header, "Sender", NULL);
+ if (tmp == NULL)
+ tmp = header_raw_find(&header, "From", NULL);
+ if (tmp != NULL) {
+ struct _header_address *addr = header_address_decode(tmp);
+
+ tmp = NULL;
+ if (addr) {
+ if (addr->type == HEADER_ADDRESS_NAME) {
+ g_string_append(out, addr->v.addr);
+ tmp = "";
+ }
+ header_address_unref(addr);
+ }
+ }
+ if (tmp == NULL) {
+ g_string_append(out, "unknown@nodomain.now.au");
+ }
+
+ /* try use the received header to get the date */
+ tmp = header_raw_find(&header, "Received", NULL);
+ if (tmp) {
+ tmp = strrchr(tmp, ';');
+ if (tmp)
+ tmp++;
+ }
+
+ /* if there isn't one, try the Date field */
+ if (tmp == NULL)
+ tmp = header_raw_find(&header, "Date", NULL);
+
+ thetime = header_decode_date(tmp, &offset);
+
+ thetime += ((offset / 100) * (60 * 60)) + (offset % 100) * 60;
+
+ /* a pseudo, but still bogus attempt at thread safing the function */
+ /*memcpy(&tm, gmtime(&thetime), sizeof(tm));*/
+ gmtime_r(&thetime, &tm);
+
+ g_string_sprintfa(out, " %s %s %d %02d:%02d:%02d %4d\n",
+ tz_days[tm.tm_wday],
+ tz_months[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900);
+
+ ret = out->str;
+ g_string_free(out, FALSE);
+ return ret;
+}
+
+static int
+mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ CamelMboxSummary *mbs = (CamelMboxSummary *)cls;
+ CamelFolderSummary *s = (CamelFolderSummary *)mbs;
+ CamelMimeParser *mp = NULL;
+ int i, count;
+ CamelMboxMessageInfo *info;
+ int fd = -1, fdout = -1;
+ off_t offset = 0;
+ char *tmpname = NULL;
+ char *buffer, *xevnew = NULL;
+ const char *xev;
+ int len;
+ guint32 uid, flags;
+ int quick = TRUE, work = FALSE;
+ struct stat st;
+ char *fromline;
+
+ /* make sure we're in sync, after this point we at least have a complete list of id's */
+ count = camel_folder_summary_count (s);
+ if (count > 0) {
+ CamelMessageInfo *mi = camel_folder_summary_index(s, count - 1);
+ summary_update(mbs, mi->content->endpos, changeinfo, ex);
+ } else {
+ summary_update(mbs, 0, changeinfo, ex);
+ }
+
+ if (camel_exception_is_set(ex))
+ return -1;
+
+ /* FIXME: This needs to take the user flags and tags fields into account */
+
+ /* check if we have any work to do */
+ d(printf("Performing sync, %d messages in inbox\n", count));
+ for (i = 0; quick && i < count; i++) {
+ info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
+ if ((expunge && (info->info.flags & CAMEL_MESSAGE_DELETED)) ||
+ (info->info.flags & CAMEL_MESSAGE_FOLDER_NOXEV))
+ quick = FALSE;
+ else
+ work |= (info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0;
+ }
+
+ d(printf("Options: %s %s %s\n", expunge ? "expunge" : "", quick ? "quick" : "", work ? "Work" : ""));
+
+ if (quick && !work)
+ return 0;
+
+ fd = open(cls->folder_path, O_RDWR);
+ if (fd == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not open folder to summarise: %s: %s"),
+ cls->folder_path, strerror(errno));
+ return -1;
+ }
+
+ mp = camel_mime_parser_new();
+ camel_mime_parser_scan_from(mp, TRUE);
+ camel_mime_parser_init_with_fd(mp, fd);
+
+ if (!quick) {
+ tmpname = alloca(strlen (cls->folder_path) + 5);
+ sprintf(tmpname, "%s.tmp", cls->folder_path);
+ d(printf("Writing tmp file to %s\n", tmpname));
+ retry_out:
+ fdout = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ if (fdout == -1) {
+ if (errno == EEXIST)
+ if (unlink(tmpname) != -1)
+ goto retry_out;
+
+ tmpname = NULL;
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot open temporary mailbox: %s"), strerror(errno));
+ goto error;
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ off_t frompos, bodypos, lastpos;
+ /* This has to be an int, not an off_t, because that's
+ * what camel_mime_parser_header returns... FIXME.
+ */
+ int xevoffset;
+
+ info = (CamelMboxMessageInfo *)camel_folder_summary_index(s, i);
+
+ g_assert(info);
+
+ d(printf("Looking at message %s\n", info->info.uid));
+
+ if (expunge && info->info.flags & CAMEL_MESSAGE_DELETED) {
+ d(printf("Deleting %s\n", info->info.uid));
+
+ g_assert(!quick);
+ offset -= (info->info.content->endpos - info->frompos);
+
+ /* FIXME: put this in folder_summary::remove()? */
+ if (cls->index)
+ ibex_unindex(cls->index, info->info.uid);
+
+ /* remove it from the change list */
+ camel_folder_change_info_remove_uid(changeinfo, info->info.uid);
+ camel_folder_summary_remove(s, (CamelMessageInfo *)info);
+ count--;
+ i--;
+ info = NULL;
+ } else if (info->info.flags & (CAMEL_MESSAGE_FOLDER_NOXEV | CAMEL_MESSAGE_FOLDER_FLAGGED)) {
+ int xevok = FALSE;
+
+ d(printf("Updating header for %s flags = %08x\n", info->info.uid, info->info.flags));
+
+ /* find the next message, header parts */
+ camel_mime_parser_seek(mp, info->frompos, SEEK_SET);
+ if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM) {
+ g_warning("camel_mime_parser_step failed (1)");
+ goto error;
+ }
+
+ if (camel_mime_parser_tell_start_from (mp) != info->frompos) {
+ g_warning("Summary/mbox mismatch, aborting sync");
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Summary mismatch, aborting sync"));
+ goto error;
+ }
+
+ if (camel_mime_parser_step (mp, &buffer, &len) == HSCAN_FROM_END) {
+ g_warning("camel_mime_parser_step failed (2)");
+ goto error;
+ }
+
+ /* Check if the X-Evolution header is valid. */
+
+ /* FIXME: Use camel_local_summary versions here */
+
+ xev = camel_mime_parser_header(mp, "X-Evolution", &xevoffset);
+ if (xev && header_evolution_decode (xev, &uid, &flags) != -1)
+ xevok = TRUE;
+
+ xevnew = header_evolution_encode(strtoul (info->info.uid, NULL, 10), info->info.flags & 0xffff);
+ if (quick) {
+ if (!xevok) {
+ g_warning("The summary told me I had an X-Evolution header, but i dont!");
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Summary mismatch, X-Evolution header missing"));
+ goto error;
+ }
+ buffer = g_strdup_printf("X-Evolution: %s", xevnew);
+ lastpos = lseek(fd, 0, SEEK_CUR);
+ lseek(fd, xevoffset, SEEK_SET);
+ do {
+ len = write(fd, buffer, strlen (buffer));
+ } while (len == -1 && errno == EINTR);
+ lseek(fd, lastpos, SEEK_SET);
+ g_free(buffer);
+ if (len == -1) {
+ goto error;
+ }
+ } else {
+ frompos = lseek(fdout, 0, SEEK_CUR);
+ fromline = camel_mbox_summary_build_from(camel_mime_parser_headers_raw (mp));
+ write(fdout, fromline, strlen(fromline));
+ g_free(fromline);
+ if (header_write(fdout, camel_mime_parser_headers_raw(mp), xevnew) == -1) {
+ d(printf("Error writing to tmp mailbox\n"));
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Error writing to temp mailbox: %s"),
+ strerror(errno));
+ goto error;
+ }
+ bodypos = lseek(fdout, 0, SEEK_CUR);
+ d(printf("pos = %d, endpos = %d, bodypos = %d\n",
+ (int) info->info.content->pos,
+ (int) info->info.content->endpos,
+ (int) info->info.content->bodypos));
+ if (copy_block(fd, fdout, info->info.content->bodypos,
+ info->info.content->endpos - info->info.content->bodypos) == -1) {
+ g_warning("Cannot copy data to output fd");
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot copy data to output file: %s"),
+ strerror (errno));
+ goto error;
+ }
+ info->frompos = frompos;
+ offset = bodypos - info->info.content->bodypos;
+ }
+ info->info.flags &= 0xffff;
+ g_free(xevnew);
+ xevnew = NULL;
+ camel_mime_parser_drop_step(mp);
+ camel_mime_parser_drop_step(mp);
+ } else {
+ if (!quick) {
+ if (copy_block(fd, fdout, info->frompos,
+ info->info.content->endpos - info->frompos) == -1) {
+ g_warning("Cannot copy data to output fd");
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot copy data to output file: %s"),
+ strerror(errno));
+ goto error;
+ }
+ /* update from pos here? */
+ info->frompos += offset;
+ } else {
+ d(printf("Nothing to do for this message\n"));
+ }
+ }
+ if (!quick && info != NULL && offset != 0) {
+ d(printf("offsetting content: %d\n", (int)offset));
+ camel_folder_summary_offset_content(info->info.content, offset);
+ d(printf("pos = %d, endpos = %d, bodypos = %d\n",
+ (int) info->info.content->pos,
+ (int) info->info.content->endpos,
+ (int) info->info.content->bodypos));
+ }
+ }
+
+ d(printf("Closing folders\n"));
+
+ if (close(fd) == -1) {
+ g_warning("Cannot close source folder: %s", strerror(errno));
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not close source folder %s: %s"),
+ cls->folder_path, strerror(errno));
+ goto error;
+ }
+
+ if (!quick) {
+ if (close(fdout) == -1) {
+ g_warning("Cannot close tmp folder: %s", strerror(errno));
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not close temp folder: %s"),
+ strerror(errno));
+ goto error;
+ }
+
+ if (rename(tmpname, cls->folder_path) == -1) {
+ g_warning("Cannot rename folder: %s", strerror(errno));
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not rename folder: %s"),
+ strerror(errno));
+ goto error;
+ }
+ tmpname = NULL;
+
+ /* TODO: move up? */
+ if (cls->index)
+ ibex_save(cls->index);
+ }
+
+ if (stat(cls->folder_path, &st) == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Unknown error: %s"),
+ strerror(errno));
+ goto error;
+ }
+
+ camel_folder_summary_touch(s);
+ s->time = st.st_mtime;
+ mbs->folder_size = st.st_size;
+ camel_folder_summary_save(s);
+
+ camel_object_unref(CAMEL_OBJECT(mp));
+
+ return 0;
+ error:
+ if (fd != -1)
+ close(fd);
+
+ if (fdout != -1)
+ close(fdout);
+
+ g_free(xevnew);
+
+ if (tmpname)
+ unlink(tmpname);
+ if (mp)
+ camel_object_unref(CAMEL_OBJECT(mp));
+
+ return -1;
+}
+
+
+
+
+
diff --git a/camel/providers/local/camel-mbox-summary.h b/camel/providers/local/camel-mbox-summary.h
new file mode 100644
index 0000000000..3999e3dcb5
--- /dev/null
+++ b/camel/providers/local/camel-mbox-summary.h
@@ -0,0 +1,63 @@
+/*
+ * 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 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_MBOX_SUMMARY_H
+#define _CAMEL_MBOX_SUMMARY_H
+
+#include "camel-local-summary.h"
+
+#define CAMEL_MBOX_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_mbox_summary_get_type (), CamelMboxSummary)
+#define CAMEL_MBOX_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mbox_summary_get_type (), CamelMboxSummaryClass)
+#define IS_CAMEL_MBOX_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_mbox_summary_get_type ())
+
+typedef struct _CamelMboxSummary CamelMboxSummary;
+typedef struct _CamelMboxSummaryClass CamelMboxSummaryClass;
+
+typedef struct _CamelMboxMessageContentInfo {
+ CamelMessageContentInfo info;
+} CamelMboxMessageContentInfo;
+
+typedef struct _CamelMboxMessageInfo {
+ CamelMessageInfo info;
+
+ off_t frompos;
+} CamelMboxMessageInfo;
+
+struct _CamelMboxSummary {
+ CamelLocalSummary parent;
+
+ struct _CamelMboxSummaryPrivate *priv;
+
+ size_t folder_size; /* size of the mbox file, last sync */
+};
+
+struct _CamelMboxSummaryClass {
+ CamelLocalSummaryClass parent_class;
+};
+
+guint camel_mbox_summary_get_type (void);
+CamelMboxSummary *camel_mbox_summary_new (const char *filename, const char *mbox_name, ibex *index);
+
+/* generate a From line from headers */
+char *camel_mbox_summary_build_from(struct _header_raw *header);
+
+#endif /* ! _CAMEL_MBOX_SUMMARY_H */
+
diff --git a/camel/providers/local/camel-mh-folder.c b/camel/providers/local/camel-mh-folder.c
new file mode 100644
index 0000000000..64f9bbee81
--- /dev/null
+++ b/camel/providers/local/camel-mh-folder.c
@@ -0,0 +1,204 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *
+ * 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
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "camel-mh-folder.h"
+#include "camel-mh-store.h"
+#include "string-utils.h"
+#include "camel-stream-fs.h"
+#include "camel-mh-summary.h"
+#include "camel-data-wrapper.h"
+#include "camel-mime-message.h"
+#include "camel-exception.h"
+
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+static CamelFolderClass *parent_class = NULL;
+
+/* Returns the class for a CamelMhFolder */
+#define CMHF_CLASS(so) CAMEL_MH_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CMHS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static CamelLocalSummary *mh_create_summary(const char *path, const char *folder, ibex *index);
+
+static void mh_append_message(CamelFolder * folder, CamelMimeMessage * message, const CamelMessageInfo *info, CamelException * ex);
+static CamelMimeMessage *mh_get_message(CamelFolder * folder, const gchar * uid, CamelException * ex);
+
+static void mh_finalize(CamelObject * object);
+
+static void camel_mh_folder_class_init(CamelObjectClass * camel_mh_folder_class)
+{
+ CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_mh_folder_class);
+ CamelLocalFolderClass *lclass = (CamelLocalFolderClass *)camel_mh_folder_class;
+
+ parent_class = CAMEL_FOLDER_CLASS (camel_type_get_global_classfuncs(camel_folder_get_type()));
+
+ /* virtual method definition */
+
+ /* virtual method overload */
+ camel_folder_class->append_message = mh_append_message;
+ camel_folder_class->get_message = mh_get_message;
+
+ lclass->create_summary = mh_create_summary;
+}
+
+static void mh_init(gpointer object, gpointer klass)
+{
+ /*CamelFolder *folder = object;
+ CamelMhFolder *mh_folder = object;*/
+}
+
+static void mh_finalize(CamelObject * object)
+{
+ /*CamelMhFolder *mh_folder = CAMEL_MH_FOLDER(object);*/
+}
+
+CamelType camel_mh_folder_get_type(void)
+{
+ static CamelType camel_mh_folder_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mh_folder_type == CAMEL_INVALID_TYPE) {
+ camel_mh_folder_type = camel_type_register(CAMEL_LOCAL_FOLDER_TYPE, "CamelMhFolder",
+ sizeof(CamelMhFolder),
+ sizeof(CamelMhFolderClass),
+ (CamelObjectClassInitFunc) camel_mh_folder_class_init,
+ NULL,
+ (CamelObjectInitFunc) mh_init,
+ (CamelObjectFinalizeFunc) mh_finalize);
+ }
+
+ return camel_mh_folder_type;
+}
+
+CamelFolder *
+camel_mh_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex)
+{
+ CamelFolder *folder;
+
+ d(printf("Creating mh folder: %s\n", full_name));
+
+ folder = (CamelFolder *)camel_object_new(CAMEL_MH_FOLDER_TYPE);
+ folder = (CamelFolder *)camel_local_folder_construct((CamelLocalFolder *)folder,
+ parent_store, full_name, flags, ex);
+
+ return folder;
+}
+
+static CamelLocalSummary *mh_create_summary(const char *path, const char *folder, ibex *index)
+{
+ return (CamelLocalSummary *)camel_mh_summary_new(path, folder, index);
+}
+
+static void mh_append_message(CamelFolder * folder, CamelMimeMessage * message, const CamelMessageInfo *info, CamelException * ex)
+{
+ CamelMhFolder *mh_folder = (CamelMhFolder *)folder;
+ CamelLocalFolder *lf = (CamelLocalFolder *)folder;
+ CamelStream *output_stream;
+ CamelMessageInfo *mi;
+ char *name;
+
+ /* FIXME: probably needs additional locking */
+
+ d(printf("Appending message\n"));
+
+ /* add it to the summary/assign the uid, etc */
+ mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex);
+ if (camel_exception_is_set(ex)) {
+ return;
+ }
+
+ d(printf("Appending message: uid is %s\n", mi->uid));
+
+ /* write it out, use the uid we got from the summary */
+ name = g_strdup_printf("%s/%s", lf->folder_path, mi->uid);
+ output_stream = camel_stream_fs_new_with_name(name, O_WRONLY|O_CREAT, 0600);
+ if (output_stream == NULL) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot append message to mh folder: %s: %s"), name, g_strerror(errno));
+ g_free(name);
+ return;
+ }
+
+ if (camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, output_stream) == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Cannot append message to mh folder: %s: %s"), name, g_strerror(errno));
+ camel_object_unref((CamelObject *)output_stream);
+ g_free(name);
+ return;
+ }
+
+ g_free(name);
+
+ camel_object_trigger_event((CamelObject *)folder, "folder_changed", ((CamelLocalFolder *)mh_folder)->changes);
+ camel_folder_change_info_clear(((CamelLocalFolder *)mh_folder)->changes);
+}
+
+static CamelMimeMessage *mh_get_message(CamelFolder * folder, const gchar * uid, CamelException * ex)
+{
+ CamelLocalFolder *lf = (CamelLocalFolder *)folder;
+ CamelStream *message_stream = NULL;
+ CamelMimeMessage *message = NULL;
+ CamelMessageInfo *info;
+ char *name;
+
+ d(printf("getting message: %s\n", uid));
+
+ /* get the message summary info */
+ if ((info = camel_folder_summary_uid((CamelFolderSummary *)lf->summary, uid)) == NULL) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message"));
+ return NULL;
+ }
+
+ name = g_strdup_printf("%s/%s", lf->folder_path, uid);
+ if ((message_stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0)) == NULL) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"),
+ name, g_strerror(errno));
+ g_free(name);
+ return NULL;
+ }
+
+ message = camel_mime_message_new();
+ if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, message_stream) == -1) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"),
+ name, _("Invalid message contents"));
+ g_free(name);
+ camel_object_unref((CamelObject *)message_stream);
+ camel_object_unref((CamelObject *)message);
+ return NULL;
+
+ }
+ camel_object_unref((CamelObject *)message_stream);
+ g_free(name);
+
+ return message;
+}
diff --git a/camel/providers/local/camel-mh-folder.h b/camel/providers/local/camel-mh-folder.h
new file mode 100644
index 0000000000..4940230952
--- /dev/null
+++ b/camel/providers/local/camel-mh-folder.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-mh-folder.h : MH folder. */
+
+/*
+ *
+ * Authors:
+ * Michael Zucchi <notzed@helixcode.com>
+ *
+ * Copyright (C) 1999 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
+ * 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_MH_FOLDER_H
+#define CAMEL_MH_FOLDER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus } */
+#include "camel-local-folder.h"
+
+#define CAMEL_MH_FOLDER_TYPE (camel_mh_folder_get_type ())
+#define CAMEL_MH_FOLDER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MH_FOLDER_TYPE, CamelMhFolder))
+#define CAMEL_MH_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MH_FOLDER_TYPE, CamelMhFolderClass))
+#define IS_CAMEL_MH_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_MH_FOLDER_TYPE))
+
+typedef struct {
+ CamelLocalFolder parent_object;
+
+} CamelMhFolder;
+
+typedef struct {
+ CamelLocalFolderClass parent_class;
+
+ /* Virtual methods */
+
+} CamelMhFolderClass;
+
+/* public methods */
+CamelFolder *camel_mh_folder_new(CamelStore *parent_store, const char *full_name, guint32 flags, CamelException *ex);
+
+/* Standard Camel function */
+CamelType camel_mh_folder_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CAMEL_MH_FOLDER_H */
diff --git a/camel/providers/local/camel-mh-store.c b/camel/providers/local/camel-mh-store.c
new file mode 100644
index 0000000000..82310578d1
--- /dev/null
+++ b/camel/providers/local/camel-mh-store.c
@@ -0,0 +1,142 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * 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 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 <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "camel-mh-store.h"
+#include "camel-mh-folder.h"
+#include "camel-exception.h"
+#include "camel-url.h"
+
+static CamelLocalStoreClass *parent_class = NULL;
+
+/* Returns the class for a CamelMhStore */
+#define CMHS_CLASS(so) CAMEL_MH_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+#define CMHF_CLASS(so) CAMEL_MH_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static CamelFolder *get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex);
+static void delete_folder(CamelStore * store, const char *folder_name, CamelException * ex);
+
+static void camel_mh_store_class_init(CamelObjectClass * camel_mh_store_class)
+{
+ CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(camel_mh_store_class);
+ /*CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(camel_mh_store_class);*/
+
+ parent_class = (CamelLocalStoreClass *)camel_type_get_global_classfuncs(camel_folder_get_type());
+
+ /* virtual method overload, use defaults for most */
+ camel_store_class->get_folder = get_folder;
+ camel_store_class->delete_folder = delete_folder;
+}
+
+static void camel_mh_store_init(CamelObject * object)
+{
+ CamelStore *store = CAMEL_STORE(object);
+
+ /* mh names are filenames, so they are case-sensitive. */
+ store->folders = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+CamelType camel_mh_store_get_type(void)
+{
+ static CamelType camel_mh_store_type = CAMEL_INVALID_TYPE;
+
+ if (camel_mh_store_type == CAMEL_INVALID_TYPE) {
+ camel_mh_store_type = camel_type_register(CAMEL_LOCAL_STORE_TYPE, "CamelMhStore",
+ sizeof(CamelMhStore),
+ sizeof(CamelMhStoreClass),
+ (CamelObjectClassInitFunc) camel_mh_store_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_mh_store_init,
+ NULL);
+ }
+
+ return camel_mh_store_type;
+}
+
+static CamelFolder *get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex)
+{
+ char *name;
+ struct stat st;
+
+ name = g_strdup_printf("%s%s", CAMEL_SERVICE(store)->url->path, folder_name);
+
+ if (stat(name, &st) == -1) {
+ if (errno != ENOENT) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not open folder `%s':\n%s"),
+ folder_name, g_strerror(errno));
+ g_free (name);
+ return NULL;
+ }
+ if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("Folder `%s' does not exist."), folder_name);
+ g_free (name);
+ return NULL;
+ }
+ printf("creating ...\n");
+
+ if (mkdir(name, 0700) != 0) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not create folder `%s':\n%s"),
+ folder_name, g_strerror(errno));
+ g_free (name);
+ return NULL;
+ }
+ printf("created ok?\n");
+
+ } else if (!S_ISDIR(st.st_mode)) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+ _("`%s' is not a directory."), name);
+ g_free (name);
+ return NULL;
+ }
+ g_free(name);
+
+ return camel_mh_folder_new(store, folder_name, flags, ex);
+}
+
+static void delete_folder(CamelStore * store, const char *folder_name, CamelException * ex)
+{
+ char *name;
+
+ /* remove folder directory - will fail if not empty */
+ name = g_strdup_printf("%s%s", CAMEL_SERVICE(store)->url->path, folder_name);
+ if (rmdir(name) == -1 && errno != ENOENT) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Could not delete folder `%s': %s"),
+ folder_name, strerror(errno));
+ g_free(name);
+ return;
+ }
+ g_free(name);
+
+ /* and remove metadata */
+ ((CamelStoreClass *)parent_class)->delete_folder(store, folder_name, ex);
+}
diff --git a/camel/providers/local/camel-mh-store.h b/camel/providers/local/camel-mh-store.h
new file mode 100644
index 0000000000..80c5ee0c28
--- /dev/null
+++ b/camel/providers/local/camel-mh-store.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-mh-store.h : class for an mh store */
+
+/*
+ *
+ * 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 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_MH_STORE_H
+#define CAMEL_MH_STORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus } */
+
+#include "camel-local-store.h"
+
+#define CAMEL_MH_STORE_TYPE (camel_mh_store_get_type ())
+#define CAMEL_MH_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MH_STORE_TYPE, CamelMhStore))
+#define CAMEL_MH_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MH_STORE_TYPE, CamelMhStoreClass))
+#define IS_CAMEL_MH_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_MH_STORE_TYPE))
+
+typedef struct {
+ CamelLocalStore parent_object;
+
+} CamelMhStore;
+
+typedef struct {
+ CamelLocalStoreClass parent_class;
+
+} CamelMhStoreClass;
+
+/* public methods */
+
+/* Standard Camel function */
+CamelType camel_mh_store_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CAMEL_MH_STORE_H */
diff --git a/camel/providers/local/camel-mh-summary.c b/camel/providers/local/camel-mh-summary.c
new file mode 100644
index 0000000000..b6b31664b4
--- /dev/null
+++ b/camel/providers/local/camel-mh-summary.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ *
+ * 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-mh-summary.h"
+#include <camel/camel-mime-message.h>
+
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <ctype.h>
+
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+#define CAMEL_MH_SUMMARY_VERSION (0x2000)
+
+static CamelMessageInfo *message_info_new(CamelFolderSummary *, struct _header_raw *);
+
+static int mh_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+static int mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
+/*static int mh_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);*/
+
+static char *mh_summary_next_uid_string(CamelFolderSummary *s);
+
+static void camel_mh_summary_class_init (CamelMhSummaryClass *class);
+static void camel_mh_summary_init (CamelMhSummary *gspaper);
+static void camel_mh_summary_finalise (CamelObject *obj);
+
+#define _PRIVATE(x) (((CamelMhSummary *)(x))->priv)
+
+struct _CamelMhSummaryPrivate {
+ char *current_uid;
+};
+
+static CamelLocalSummaryClass *parent_class;
+
+CamelType
+camel_mh_summary_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register(camel_local_summary_get_type (), "CamelMhSummary",
+ sizeof(CamelMhSummary),
+ sizeof(CamelMhSummaryClass),
+ (CamelObjectClassInitFunc)camel_mh_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc)camel_mh_summary_init,
+ (CamelObjectFinalizeFunc)camel_mh_summary_finalise);
+ }
+
+ return type;
+}
+
+static void
+camel_mh_summary_class_init (CamelMhSummaryClass *class)
+{
+ CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *) class;
+ CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)class;
+
+ parent_class = (CamelLocalSummaryClass *)camel_type_get_global_classfuncs(camel_local_summary_get_type ());
+
+ /* override methods */
+ sklass->message_info_new = message_info_new;
+ sklass->next_uid_string = mh_summary_next_uid_string;
+
+ lklass->check = mh_summary_check;
+ lklass->sync = mh_summary_sync;
+ /*lklass->add = mh_summary_add;*/
+}
+
+static void
+camel_mh_summary_init (CamelMhSummary *o)
+{
+ struct _CamelFolderSummary *s = (CamelFolderSummary *) o;
+
+ o->priv = g_malloc0(sizeof(*o->priv));
+ /* set unique file version */
+ s->version += CAMEL_MH_SUMMARY_VERSION;
+}
+
+static void
+camel_mh_summary_finalise(CamelObject *obj)
+{
+ CamelMhSummary *o = (CamelMhSummary *)obj;
+
+ g_free(o->priv);
+}
+
+/**
+ * camel_mh_summary_new:
+ *
+ * Create a new CamelMhSummary object.
+ *
+ * Return value: A new #CamelMhSummary object.
+ **/
+CamelMhSummary *camel_mh_summary_new (const char *filename, const char *mhdir, ibex *index)
+{
+ CamelMhSummary *o = (CamelMhSummary *)camel_object_new(camel_mh_summary_get_type ());
+
+ camel_local_summary_construct((CamelLocalSummary *)o, filename, mhdir, index);
+ return o;
+}
+
+static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header_raw *h)
+{
+ CamelMessageInfo *mi;
+ /*CamelMhSummary *mhs = (CamelMhSummary *)s;*/
+
+ mi = ((CamelFolderSummaryClass *) parent_class)->message_info_new(s, h);
+ /* hmm, this isn't quite right */
+ if (mi) {
+ /*mi->uid = mh_summary_next_uid_string(s);*/
+ }
+
+ return mi;
+}
+
+static char *mh_summary_next_uid_string(CamelFolderSummary *s)
+{
+ CamelMhSummary *mhs = (CamelMhSummary *)s;
+ CamelLocalSummary *cls = (CamelLocalSummary *)s;
+ int fd = -1;
+ guint32 uid;
+ char *name = NULL;
+
+ /* if we are working to add an existing file, then use current_uid */
+ if (mhs->priv->current_uid)
+ return g_strdup(mhs->priv->current_uid);
+
+ /* else scan for one - and create it too, to make sure */
+ do {
+ g_free(name);
+ close(fd);
+ uid = camel_folder_summary_next_uid(s);
+ name = g_strdup_printf("%s/%u", cls->folder_path, uid);
+ /* O_EXCL isn't guaranteed, sigh. Oh well, bad luck, mh has problems anyway */
+ fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ } while (fd == -1 && errno == EEXIST);
+
+ close(fd);
+
+ return g_strdup_printf("%u", uid);
+}
+
+static int camel_mh_summary_add(CamelLocalSummary *cls, const char *name, int forceindex)
+{
+ CamelMhSummary *mhs = (CamelMhSummary *)cls;
+ char *filename = g_strdup_printf("%s/%s", cls->folder_path, name);
+ int fd;
+ CamelMimeParser *mp;
+
+ d(printf("summarising: %s\n", name));
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ g_warning("Cannot summarise/index: %s: %s", filename, strerror(errno));
+ g_free(filename);
+ return -1;
+ }
+ mp = camel_mime_parser_new();
+ camel_mime_parser_scan_from(mp, FALSE);
+ camel_mime_parser_init_with_fd(mp, fd);
+ if (cls->index && (forceindex || !ibex_contains_name(cls->index, (char *)name))) {
+ d(printf("forcing indexing of message content\n"));
+ camel_folder_summary_set_index((CamelFolderSummary *)mhs, cls->index);
+ } else {
+ camel_folder_summary_set_index((CamelFolderSummary *)mhs, NULL);
+ }
+ mhs->priv->current_uid = (char *)name;
+ camel_folder_summary_add_from_parser((CamelFolderSummary *)mhs, mp);
+ camel_object_unref((CamelObject *)mp);
+ mhs->priv->current_uid = NULL;
+ camel_folder_summary_set_index((CamelFolderSummary *)mhs, NULL);
+ g_free(filename);
+ return 0;
+}
+
+static void
+remove_summary(char *key, CamelMessageInfo *info, CamelLocalSummary *cls)
+{
+ d(printf("removing message %s from summary\n", key));
+ if (cls->index)
+ ibex_unindex(cls->index, info->uid);
+ camel_folder_summary_remove((CamelFolderSummary *)cls, info);
+}
+
+static int
+mh_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex)
+{
+ DIR *dir;
+ struct dirent *d;
+ char *p, c;
+ CamelMessageInfo *info;
+ GHashTable *left;
+ int i, count;
+ int forceindex;
+
+ /* FIXME: Handle changeinfo */
+
+ d(printf("checking summary ...\n"));
+
+ /* scan the directory, check for mail files not in the index, or index entries that
+ no longer exist */
+ dir = opendir(cls->folder_path);
+ if (dir == NULL) {
+ camel_exception_setv(ex, 1, "Cannot open MH directory path: %s: %s", cls->folder_path, strerror(errno));
+ return -1;
+ }
+
+ /* keeps track of all uid's that have not been processed */
+ left = g_hash_table_new(g_str_hash, g_str_equal);
+ count = camel_folder_summary_count((CamelFolderSummary *)cls);
+ forceindex = count == 0;
+ for (i=0;i<count;i++) {
+ info = camel_folder_summary_index((CamelFolderSummary *)cls, i);
+ if (info) {
+ g_hash_table_insert(left, info->uid, info);
+ }
+ }
+
+ while ( (d = readdir(dir)) ) {
+ /* FIXME: also run stat to check for regular file */
+ p = d->d_name;
+ while ( (c = *p++) ) {
+ if (!isdigit(c))
+ break;
+ }
+ if (c==0) {
+ info = camel_folder_summary_uid((CamelFolderSummary *)cls, d->d_name);
+ if (info == NULL || (cls->index && (!ibex_contains_name(cls->index, d->d_name)))) {
+ /* need to add this file to the summary */
+ if (info != NULL) {
+ g_hash_table_remove(left, info->uid);
+ camel_folder_summary_remove((CamelFolderSummary *)cls, info);
+ }
+ camel_mh_summary_add(cls, d->d_name, forceindex);
+ } else {
+ g_hash_table_remove(left, info->uid);
+ }
+ }
+ }
+ closedir(dir);
+ g_hash_table_foreach(left, (GHFunc)remove_summary, cls);
+ g_hash_table_destroy(left);
+
+ /* FIXME: move this up a class */
+
+ /* force a save of the index, just to make sure */
+ /* note this could be expensive so possibly shouldn't be here
+ as such */
+ if (cls->index) {
+ ibex_save(cls->index);
+ }
+
+ return 0;
+}
+
+
+
+/* sync the summary with the ondisk files.
+ It doesnt store the state in the file, the summary only, == MUCH faster */
+static int
+mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changes, CamelException *ex)
+{
+ int count, i;
+ CamelMessageInfo *info;
+ char *name;
+
+ d(printf("summary_sync(expunge=%s)\n", expunge?"true":"false"));
+
+ if (cls->index) {
+ ibex_save(cls->index);
+ }
+ if (!expunge)
+ return 0;
+
+ count = camel_folder_summary_count((CamelFolderSummary *)cls);
+ for (i=count-1;i>=0;i--) {
+ info = camel_folder_summary_index((CamelFolderSummary *)cls, i);
+ if (info && info->flags & CAMEL_MESSAGE_DELETED) {
+ name = g_strdup_printf("%s/%s", cls->folder_path, info->uid);
+ d(printf("deleting %s\n", name));
+ if (unlink(name) == 0 || errno==ENOENT) {
+
+ /* FIXME: put this in folder_summary::remove()? */
+ if (cls->index)
+ ibex_unindex(cls->index, info->uid);
+
+ camel_folder_change_info_remove_uid(changes, info->uid);
+ camel_folder_summary_remove((CamelFolderSummary *)cls, info);
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/camel/providers/local/camel-mh-summary.h b/camel/providers/local/camel-mh-summary.h
new file mode 100644
index 0000000000..41873d5f3b
--- /dev/null
+++ b/camel/providers/local/camel-mh-summary.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ *
+ * 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_MH_SUMMARY_H
+#define _CAMEL_MH_SUMMARY_H
+
+#include "camel-local-summary.h"
+#include <camel/camel-folder.h>
+#include <camel/camel-exception.h>
+#include <libibex/ibex.h>
+
+#define CAMEL_MH_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_mh_summary_get_type (), CamelMhSummary)
+#define CAMEL_MH_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_mh_summary_get_type (), CamelMhSummaryClass)
+#define IS_CAMEL_MH_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_mh_summary_get_type ())
+
+typedef struct _CamelMhSummary CamelMhSummary;
+typedef struct _CamelMhSummaryClass CamelMhSummaryClass;
+
+struct _CamelMhSummary {
+ CamelLocalSummary parent;
+ struct _CamelMhSummaryPrivate *priv;
+};
+
+struct _CamelMhSummaryClass {
+ CamelLocalSummaryClass parent_class;
+
+ /* virtual methods */
+
+ /* signals */
+};
+
+CamelType camel_mh_summary_get_type (void);
+CamelMhSummary *camel_mh_summary_new (const char *filename, const char *mhdir, ibex *index);
+
+#endif /* ! _CAMEL_MH_SUMMARY_H */
+
diff --git a/camel/providers/local/libcamellocal.urls b/camel/providers/local/libcamellocal.urls
new file mode 100644
index 0000000000..141aa7caf5
--- /dev/null
+++ b/camel/providers/local/libcamellocal.urls
@@ -0,0 +1,2 @@
+mh
+mbox