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