Initial cut for local provider, to handle mh/mailbox/maildir at
authorMichael Zucci <zucchi@src.gnome.org>
Wed, 15 Nov 2000 03:04:12 +0000 (03:04 +0000)
committerMichael Zucci <zucchi@src.gnome.org>
Wed, 15 Nov 2000 03:04:12 +0000 (03:04 +0000)
least.
Checking in to make a backup.

22 files changed:
camel/providers/local/Makefile.am [new file with mode: 0644]
camel/providers/local/camel-local-folder.c [new file with mode: 0644]
camel/providers/local/camel-local-folder.h [new file with mode: 0644]
camel/providers/local/camel-local-provider.c [new file with mode: 0644]
camel/providers/local/camel-local-store.c [new file with mode: 0644]
camel/providers/local/camel-local-store.h [new file with mode: 0644]
camel/providers/local/camel-local-summary.c [new file with mode: 0644]
camel/providers/local/camel-local-summary.h [new file with mode: 0644]
camel/providers/local/camel-mbox-folder.c [new file with mode: 0644]
camel/providers/local/camel-mbox-folder.h [new file with mode: 0644]
camel/providers/local/camel-mbox-provider.c [new file with mode: 0644]
camel/providers/local/camel-mbox-store.c [new file with mode: 0644]
camel/providers/local/camel-mbox-store.h [new file with mode: 0644]
camel/providers/local/camel-mbox-summary.c [new file with mode: 0644]
camel/providers/local/camel-mbox-summary.h [new file with mode: 0644]
camel/providers/local/camel-mh-folder.c [new file with mode: 0644]
camel/providers/local/camel-mh-folder.h [new file with mode: 0644]
camel/providers/local/camel-mh-store.c [new file with mode: 0644]
camel/providers/local/camel-mh-store.h [new file with mode: 0644]
camel/providers/local/camel-mh-summary.c [new file with mode: 0644]
camel/providers/local/camel-mh-summary.h [new file with mode: 0644]
camel/providers/local/libcamellocal.urls [new file with mode: 0644]

diff --git a/camel/providers/local/Makefile.am b/camel/providers/local/Makefile.am
new file mode 100644 (file)
index 0000000..01a3072
--- /dev/null
@@ -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 (file)
index 0000000..034f91d
--- /dev/null
@@ -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 (file)
index 0000000..96e4b23
--- /dev/null
@@ -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 (file)
index 0000000..f0beec0
--- /dev/null
@@ -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 (file)
index 0000000..c07a1d2
--- /dev/null
@@ -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 (file)
index 0000000..0619184
--- /dev/null
@@ -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 (file)
index 0000000..68c8340
--- /dev/null
@@ -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 (file)
index 0000000..f1816e0
--- /dev/null
@@ -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 (file)
index 0000000..d12b643
--- /dev/null
@@ -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 (file)
index 0000000..573b5df
--- /dev/null
@@ -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 (file)
index 0000000..bfce8b5
--- /dev/null
@@ -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 (file)
index 0000000..77a2916
--- /dev/null
@@ -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 (file)
index 0000000..29e0e91
--- /dev/null
@@ -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 (file)
index 0000000..32bf411
--- /dev/null
@@ -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 (file)
index 0000000..3999e3d
--- /dev/null
@@ -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 (file)
index 0000000..64f9bbe
--- /dev/null
@@ -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 (file)
index 0000000..4940230
--- /dev/null
@@ -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 (file)
index 0000000..8231057
--- /dev/null
@@ -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 (file)
index 0000000..80c5ee0
--- /dev/null
@@ -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 (file)
index 0000000..b6b3166
--- /dev/null
@@ -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 (file)
index 0000000..41873d5
--- /dev/null
@@ -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 (file)
index 0000000..141aa7c
--- /dev/null
@@ -0,0 +1,2 @@
+mh
+mbox