From 383f245d39b8b806a4b6ce4dea7b0ab7f1658450 Mon Sep 17 00:00:00 2001 From: NotZed Date: Thu, 20 Apr 2000 23:48:45 +0000 Subject: MERGE NEW_PARSER branch into HEAD, fixed conflicts. 2000-04-20 NotZed * MERGE NEW_PARSER branch into HEAD, fixed conflicts. * gmime-content-field.c (_print_parameter): Duh, removed again (@@#$@ cvs merge). * camel-mime-utils.c (header_content_type_is): Constify. (header_content_type_unref): Killed a couple warnings. * camel-folder.c (_init): Removed more log crap. * providers/Makefile.am (SUBDIRS): Removed nntp, pending fixes for summary changes. * providers/mbox/camel-mbox-folder.c (_get_message_by_number): Fixed for new summary interface. Added a warning for using this broken api. (_get_message_by_uid): Fixed for message new with session vanishing. svn path=/trunk/; revision=2531 --- camel/providers/mbox/Makefile.am | 6 +- camel/providers/mbox/camel-mbox-folder.c | 543 ++++------ camel/providers/mbox/camel-mbox-folder.h | 7 +- camel/providers/mbox/camel-mbox-search.c | 35 +- camel/providers/mbox/camel-mbox-summary.c | 1556 ++++++++++++++++++++++------- camel/providers/mbox/camel-mbox-summary.h | 115 +-- camel/providers/mbox/camel-mbox-utils.c | 52 - 7 files changed, 1469 insertions(+), 845 deletions(-) (limited to 'camel/providers/mbox') diff --git a/camel/providers/mbox/Makefile.am b/camel/providers/mbox/Makefile.am index e3234ce228..4dbe3ed93d 100644 --- a/camel/providers/mbox/Makefile.am +++ b/camel/providers/mbox/Makefile.am @@ -24,22 +24,22 @@ libcamelmbox_la_SOURCES = \ camel-mbox-parser.c \ camel-mbox-provider.c \ camel-mbox-store.c \ - camel-mbox-summary.c \ camel-mbox-search.c \ + camel-mbox-summary.c \ camel-mbox-utils.c libcamelmboxinclude_HEADERS = \ camel-mbox-folder.h \ camel-mbox-parser.h \ camel-mbox-store.h \ - camel-mbox-summary.h \ camel-mbox-search.h \ + camel-mbox-summary.h \ camel-mbox-utils.h - libcamelmbox_la_LDFLAGS = -version-info 0:0:0 -rpath $(libdir) libcamelmbox_la_LIBADD = $(top_builddir)/e-util/libeutil.la $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS) #libcamelmbox_la_LIBADD = $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS) EXTRA_DIST = + diff --git a/camel/providers/mbox/camel-mbox-folder.c b/camel/providers/mbox/camel-mbox-folder.c index 9cd055a658..2fa3ddc9da 100644 --- a/camel/providers/mbox/camel-mbox-folder.c +++ b/camel/providers/mbox/camel-mbox-folder.c @@ -2,9 +2,10 @@ /* camel-mbox-folder.c : Abstract class for an email folder */ /* - * Author : Bertrand Guiheneuf + * Authors: Bertrand Guiheneuf + * Michael Zucchi * - * Copyright (C) 1999 Helix Code . + * Copyright (C) 1999, 2000 Helix Code Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -48,6 +49,8 @@ #include "camel-exception.h" +#define d(x) + static CamelFolderClass *parent_class=NULL; /* Returns the class for a CamelMboxFolder */ @@ -59,8 +62,6 @@ static CamelFolderClass *parent_class=NULL; static void _init (CamelFolder *folder, CamelStore *parent_store, CamelFolder *parent_folder, const gchar *name, gchar separator, CamelException *ex); -static void _set_name(CamelFolder *folder, const gchar *name, CamelException *ex); - static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex); static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex); @@ -80,6 +81,8 @@ static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, Ca static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex); #endif +GPtrArray *summary_get_message_info (CamelFolder *folder, int first, int count); + static void _finalize (GtkObject *object); static void @@ -94,7 +97,6 @@ camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class) /* virtual method overload */ camel_folder_class->init = _init; - camel_folder_class->set_name = _set_name; camel_folder_class->open = _open; camel_folder_class->close = _close; camel_folder_class->exists = _exists; @@ -117,12 +119,12 @@ camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class) camel_folder_class->search_complete = camel_mbox_folder_search_complete; camel_folder_class->search_cancel = camel_mbox_folder_search_cancel; + camel_folder_class->get_message_info = summary_get_message_info; + gtk_object_class->finalize = _finalize; } - - static void _finalize (GtkObject *object) { @@ -134,10 +136,6 @@ _finalize (GtkObject *object) GTK_OBJECT_CLASS (parent_class)->finalize (object); } - - - - GtkType camel_mbox_folder_get_type (void) { @@ -162,154 +160,73 @@ camel_mbox_folder_get_type (void) return camel_mbox_folder_type; } - - - - - static void _init (CamelFolder *folder, CamelStore *parent_store, CamelFolder *parent_folder, const gchar *name, gchar separator, CamelException *ex) { + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; + const gchar *root_dir_path; + /* call parent method */ parent_class->init (folder, parent_store, parent_folder, name, separator, ex); - if (camel_exception_get_id (ex)) return; + if (camel_exception_get_id (ex)) + return; /* we assume that the parent init method checks for the existance of @folder */ - folder->can_hold_messages = TRUE; folder->can_hold_folders = TRUE; folder->has_summary_capability = TRUE; folder->has_uid_capability = TRUE; folder->has_search_capability = TRUE; - folder->summary = NULL; -} - + mbox_folder->summary = NULL; -/* internal method used to : - - test for the existence of a summary file - - test the sync between the summary and the mbox file - - load the summary or create it if necessary -*/ -static void -_check_get_or_maybe_generate_summary_file (CamelMboxFolder *mbox_folder, - CamelException *ex) -{ - CamelFolder *folder = CAMEL_FOLDER (mbox_folder); - CamelMboxSummary *summ; - GArray *message_info_array; - gint mbox_file_fd; - guint32 next_uid; - guint32 file_size; - struct stat st; - - folder->summary = NULL; - - /* Test for the existence and up-to-dateness of the summary file. */ - if (access (mbox_folder->summary_file_path, F_OK) == 0) { - summ = camel_mbox_summary_load (mbox_folder->summary_file_path, - ex); - if (summ) { - if (stat (mbox_folder->folder_file_path, &st) == 0 && - summ->mbox_file_size == st.st_size && - summ->mbox_modtime == st.st_mtime) - folder->summary = CAMEL_FOLDER_SUMMARY (summ); - else - gtk_object_destroy (GTK_OBJECT (summ)); - } else { - /* Bad summary file */ - if (camel_exception_get_id (ex) != - CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID) - return; - camel_exception_clear (ex); - } - } - - /* In the case where the summary does not exist (or was the - * wrong version), or is not in sync with the mbox file, - * regenerate it. - */ - if (folder->summary == NULL) { - /* Parse the mbox folder and get some information - * about the messages. - */ - mbox_file_fd = open (mbox_folder->folder_file_path, O_RDONLY); - if (mbox_file_fd != -1) { - message_info_array = - camel_mbox_parse_file (mbox_file_fd, "From ", - 0, &file_size, - &next_uid, TRUE, - NULL, 0, ex); - close (mbox_file_fd); - if (camel_exception_get_id (ex)) - return; - - next_uid = camel_mbox_write_xev (mbox_folder, - mbox_folder->folder_file_path, - message_info_array, - &file_size, - next_uid, ex); - if (camel_exception_get_id (ex)) { - /* ** FIXME : free the preparsed information */ - return; - } + /* now set the name info */ + g_free (mbox_folder->folder_file_path); + g_free (mbox_folder->folder_dir_path); + g_free (mbox_folder->index_file_path); - summ = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL)); - summ->message_info = parsed_information_to_mbox_summary (message_info_array); - summ->nb_message = summ->message_info->len; - summ->next_uid = next_uid; - summ->mbox_file_size = file_size; - /* **FIXME : Free the parsed information structure */ - } else { - summ = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL)); - summ->message_info = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation)); - summ->nb_message = 0; - summ->next_uid = 0; - summ->mbox_file_size = 0; - } + root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store)); - folder->summary = CAMEL_FOLDER_SUMMARY (summ); - } + mbox_folder->folder_file_path = g_strdup_printf ("%s/%s", root_dir_path, folder->full_name); + mbox_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->full_name); + mbox_folder->folder_dir_path = g_strdup_printf ("%s/%s.sdb", root_dir_path, folder->full_name); + mbox_folder->index_file_path = g_strdup_printf ("%s/%s.ibex", root_dir_path, folder->full_name); } - - static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex) { CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); + /* call parent class */ + parent_class->open (folder, mode, ex); + if (camel_exception_get_id(ex)) + return; + mbox_folder->index = ibex_open(mbox_folder->index_file_path, O_CREAT|O_RDWR, 0600); if (mbox_folder->index == NULL) { g_warning("Could not open/create index file: %s: indexing will not function", strerror(errno)); } - /* call parent class */ - parent_class->open (folder, mode, ex); - if (camel_exception_get_id(ex)) + mbox_folder->summary = camel_mbox_summary_new(mbox_folder->summary_file_path, mbox_folder->folder_file_path, mbox_folder->index); + if (mbox_folder->summary == NULL) { + camel_exception_set (ex, + CAMEL_EXCEPTION_FOLDER_INVALID, /* FIXME: right error code */ + "Could not create summary"); return; - -#if 0 - /* get (or create) uid list */ - if (!(mbox_load_uid_list (mbox_folder) > 0)) - mbox_generate_uid_list (mbox_folder); -#endif - - _check_get_or_maybe_generate_summary_file (mbox_folder, ex); + } + camel_mbox_summary_load(mbox_folder->summary); } - static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex) { CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); - CamelMboxSummary *mbox_summary = CAMEL_MBOX_SUMMARY (folder->summary); - struct stat st; /* call parent implementation */ parent_class->close (folder, expunge, ex); @@ -317,47 +234,15 @@ _close (CamelFolder *folder, gboolean expunge, CamelException *ex) /* save index */ if (mbox_folder->index) { ibex_close(mbox_folder->index); + mbox_folder->index = NULL; } - - /* Update the summary and save it to disk */ - if (stat (mbox_folder->folder_file_path, &st) == 0) { - mbox_summary->mbox_file_size = st.st_size; - mbox_summary->mbox_modtime = st.st_mtime; - } - camel_mbox_summary_save (mbox_summary, - mbox_folder->summary_file_path, ex); -} - - - - -static void -_set_name (CamelFolder *folder, const gchar *name, CamelException *ex) -{ - CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); - const gchar *root_dir_path; - - /* call default implementation */ - parent_class->set_name (folder, name, ex); - if (camel_exception_get_id (ex)) return; - - g_free (mbox_folder->folder_file_path); - g_free (mbox_folder->folder_dir_path); - g_free (mbox_folder->index_file_path); - - root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store)); - - mbox_folder->folder_file_path = g_strdup_printf ("%s/%s", root_dir_path, folder->full_name); - mbox_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->full_name); - mbox_folder->folder_dir_path = g_strdup_printf ("%s/%s.sdb", root_dir_path, folder->full_name); - mbox_folder->index_file_path = g_strdup_printf ("%s/%s.ibex", root_dir_path, folder->full_name); + camel_mbox_summary_save (mbox_folder->summary); + camel_mbox_summary_unref (mbox_folder->summary); + mbox_folder->summary = NULL; } - - - - +/* FIXME: clean up this snot */ static gboolean _exists (CamelFolder *folder, CamelException *ex) { @@ -420,18 +305,11 @@ _exists (CamelFolder *folder, CamelException *ex) return exists; } - - - - - - - +/* FIXME: clean up this snot */ static gboolean _create (CamelFolder *folder, CamelException *ex) { CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); - CamelMboxSummary *summary; const gchar *folder_file_path, *folder_dir_path; mode_t dir_mode = S_IRWXU; gint mkdir_error; @@ -480,13 +358,6 @@ _create (CamelFolder *folder, CamelException *ex) close (creat_fd); - /* create the summary object */ - summary = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL)); - summary->nb_message = 0; - summary->next_uid = 1; - summary->mbox_file_size = 0; - summary->message_info = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation)); - return TRUE; /* exception handling for io errors */ @@ -505,7 +376,7 @@ _create (CamelFolder *folder, CamelException *ex) } - +/* FIXME: cleanup */ static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex) { @@ -605,9 +476,7 @@ _delete (CamelFolder *folder, gboolean recurse, CamelException *ex) return TRUE; } - - - +/* TODO: remove this */ gboolean _delete_messages (CamelFolder *folder, CamelException *ex) { @@ -616,7 +485,6 @@ _delete_messages (CamelFolder *folder, CamelException *ex) const gchar *folder_file_path; gboolean folder_already_exists; int creat_fd; - g_assert(folder!=NULL); /* in the case where the folder does not exist, @@ -667,7 +535,7 @@ _delete_messages (CamelFolder *folder, CamelException *ex) } - +/* FIXME: cleanup */ static GList * _list_subfolders (CamelFolder *folder, CamelException *ex) { @@ -786,138 +654,133 @@ _list_subfolders (CamelFolder *folder, CamelException *ex) return NULL; } - - - static gint _get_message_count (CamelFolder *folder, CamelException *ex) { - gint message_count; + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; g_assert (folder); - g_assert (folder->summary); + g_assert (mbox_folder->summary); - message_count = CAMEL_MBOX_SUMMARY (folder->summary)->nb_message; - - return message_count; + return camel_mbox_summary_message_count(mbox_folder->summary); } +/* + This is a lazy append. + Basically, messages are appended to the end of the mbox, and probably assigned + a new uid (they wont be if copying from a source folder which doesn't have + a uid - which wont happen with the current summariser). + + Indexing/summarising happens when the mbox is next queried. + + Should this set a flag up for subsequent updating?? +*/ + +/* FIXME: this may need some tweaking for performance? */ static void _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex) { - CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); - CamelMboxSummary *summary = CAMEL_MBOX_SUMMARY (folder->summary); + CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder), *source_folder; CamelStream *output_stream; - guint32 tmp_file_size; - guint32 next_uid; - gint tmp_file_fd; - GArray *message_info_array; - GArray *mbox_summary_info; - gchar *tmp_message_filename; - gint fd1, fd2; - int i; - - tmp_message_filename = g_strdup_printf ("%s.tmp", - mbox_folder->folder_file_path); - - /* write the message itself */ - output_stream = camel_stream_fs_new_with_name (tmp_message_filename, - CAMEL_STREAM_FS_WRITE); - if (output_stream != NULL) { - camel_stream_write_string (output_stream, "From - \n"); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream); - } - camel_stream_close (output_stream); - gtk_object_unref (GTK_OBJECT (output_stream)); - - /* at this point we have saved the message to a - temporary file, now, we have to add the x-evolution - field and also update the main summary */ - - /* - First : parse the mbox file, but only from the - position where the message has been added, - wich happens to be the last postion in the - mbox file before we added the message. - This position is still stored in the summary - for the moment - */ - next_uid = summary->next_uid; - tmp_file_fd = open (tmp_message_filename, O_RDONLY); - message_info_array = - camel_mbox_parse_file (tmp_file_fd, "From - ", 0, - &tmp_file_size, &next_uid, TRUE, - NULL, 0, ex); - - close (tmp_file_fd); - - /* get the value of the last available UID - as saved in the summary file, again */ - next_uid = summary->next_uid; - - /* make sure all our of message info's have 0 uid - ignore any - set elsewhere */ - for (i=0;ilen;i++) { - g_array_index(message_info_array, CamelMboxParserMessageInfo, i).uid = 0; - } + struct stat st; + off_t seek; + char *xev; + guint32 uid; - /* - OK, this is not very efficient, we should not use the same - method as for parsing an entire mail file, - but I have no time to write a simpler parser - */ - next_uid = camel_mbox_write_xev (mbox_folder, tmp_message_filename, - message_info_array, &tmp_file_size, next_uid, ex); - - if (camel_exception_get_id (ex)) { - /* ** FIXME : free the preparsed information */ + if (stat(mbox_folder->folder_file_path, &st) != 0) { + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */ + "Cannot append to mbox file: %s", strerror (errno)); return; } - mbox_summary_info = - parsed_information_to_mbox_summary (message_info_array); + /* are we coming from an mbox folder? then we can optimise somewhat ... */ + if (message->folder && IS_CAMEL_MBOX_FOLDER(message->folder)) { + CamelMboxMessageInfo *info; + int sfd, dfd; + off_t pos; - /* store the number of messages as well as the summary array */ - summary->nb_message += 1; - summary->next_uid = next_uid; + /* FIXME: this is pretty ugly - we lookup the message info in the source folder, copy it, + then go back and paste in its real uid. */ + source_folder = (CamelMboxFolder *)message->folder; + info = camel_mbox_summary_uid(source_folder->summary, message->message_uid); - ((CamelMboxSummaryInformation *)(mbox_summary_info->data))->position += - summary->mbox_file_size; - summary->mbox_file_size += tmp_file_size; + d(printf("Copying message directly from %s to %s\n", source_folder->folder_file_path, mbox_folder->folder_file_path)); + d(printf("start = %d, xev = %d\n", ((CamelMboxMessageContentInfo *)info->info.content)->pos, info->xev_offset)); - camel_mbox_summary_append_entries (summary, mbox_summary_info); - g_array_free (mbox_summary_info, TRUE); - + sfd = open(source_folder->folder_file_path, O_RDONLY); + dfd = open(mbox_folder->folder_file_path, O_RDWR|O_CREAT, 0600); + if (lseek(dfd, st.st_size, SEEK_SET) != st.st_size) { + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */ + "Cannot append to mbox file: %s", strerror (errno)); + close(sfd); + close(dfd); + return; + } + write(dfd, "From - \n", strlen("From - \n")); + camel_mbox_summary_copy_block + (sfd, dfd, ((CamelMboxMessageContentInfo *)info->info.content)->pos, + ((CamelMboxMessageContentInfo *)info->info.content)->endpos - ((CamelMboxMessageContentInfo *)info->info.content)->pos); + if (info->xev_offset != -1) { + pos = st.st_size + (info->xev_offset - ((CamelMboxMessageContentInfo *)info->info.content)->pos) + strlen("From - \n"); + d(printf("Inserting new uid at %d\n", (int)pos)); + if (pos != lseek(dfd, pos, SEEK_SET)) { + ftruncate(dfd, st.st_size); + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */ + "Cannot append to mbox file: %s", strerror (errno)); + close(sfd); + close(dfd); + return; + } + uid = camel_mbox_summary_next_uid(mbox_folder->summary); + xev = g_strdup_printf("X-Evolution: %08x-%04x", uid, 0); + write(dfd, xev, strlen(xev)); /* FIXME: check return */ + d(printf("header = %s\n", xev)); + g_free(xev); + } + close(sfd); + close(dfd); + return; + } - /* append the temporary file message to the mbox file */ - fd1 = open (tmp_message_filename, O_RDONLY); - fd2 = open (mbox_folder->folder_file_path, - O_WRONLY | O_CREAT | O_APPEND, - 0600); + /* its not an mbox folder, so lets do it the slow way ... */ + output_stream = camel_stream_fs_new_with_name (mbox_folder->folder_file_path, CAMEL_STREAM_FS_WRITE); + if (output_stream == NULL) { + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */ + "Cannot append to mbox file: %s", strerror (errno)); + return; + } - if (fd2 == -1) { + seek = camel_seekable_stream_seek((CamelSeekableStream *)output_stream, st.st_size, SEEK_SET); + if (seek != st.st_size) { camel_exception_setv (ex, - CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, - "could not open the mbox folder file for appending the message\n" - "\t%s\n" - "Full error is : %s\n", - mbox_folder->folder_file_path, - strerror (errno)); + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, /* FIXME: what code? */ + "Cannot seek to position in mbox file: %s", strerror (errno)); + gtk_object_unref ((GtkObject *)output_stream); return; } - camel_mbox_copy_file_chunk (fd1, - fd2, - tmp_file_size, - ex); - close (fd1); - close (fd2); + /* assign a new x-evolution header */ + /* FIXME: save flags? */ + camel_medium_remove_header((CamelMedium *)message, "X-Evolution"); + uid = camel_mbox_summary_next_uid(mbox_folder->summary); + xev = g_strdup_printf("%08x-%04x", uid, 0); + camel_medium_add_header((CamelMedium *)message, "X-Evolution", xev); + g_free(xev); + + camel_stream_write_string (output_stream, "From - \n"); + /* FIXME: does this return an error? IT HAS TO FOR THIS TO BE RELIABLE */ + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream); + camel_stream_close (output_stream); - /* remove the temporary file */ - unlink (tmp_message_filename); + /* TODO: update the summary so it knows a new message is there to summarise/index */ + /* This is only a performance improvement, the summary is *only* a cache */ - g_free (tmp_message_filename); + gtk_object_unref (GTK_OBJECT (output_stream)); } @@ -926,109 +789,95 @@ _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException static GList * _get_uid_list (CamelFolder *folder, CamelException *ex) { - GArray *message_info_array; - CamelMboxSummaryInformation *message_info; GList *uid_list = NULL; - int i; - - message_info_array = - CAMEL_MBOX_SUMMARY (folder->summary)->message_info; - - for (i=0; ilen; i++) { - message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i; - uid_list = g_list_prepend (uid_list, g_strdup_printf ("%u", message_info->uid)); + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; + int i, count; + + /* FIXME: how are these allocated strings ever free'd? */ + count = camel_mbox_summary_message_count(mbox_folder->summary); + for (i=0;isummary, i); + uid_list = g_list_prepend(uid_list, g_strdup(info->info.uid)); } return uid_list; } - - - static CamelMimeMessage * _get_message_by_number (CamelFolder *folder, gint number, CamelException *ex) { - GArray *message_info_array; - CamelMboxSummaryInformation *message_info; - char uidbuf[20]; + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; + CamelMboxMessageInfo *info; - message_info_array = - CAMEL_MBOX_SUMMARY (folder->summary)->message_info; + g_warning("YOUR CODE SHOULD NOT BE GETTING MESSAGES BY NUMBER, CHANGE IT"); - if (number > message_info_array->len) { + info = camel_mbox_summary_index(mbox_folder->summary, number); + if (info == NULL) { camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, "No such message %d in folder `%s'.", number, folder->name); return NULL; } - message_info = - (CamelMboxSummaryInformation *)(message_info_array->data) + - (number - 1); - sprintf (uidbuf, "%lu", message_info->uid); - - return _get_message_by_uid (folder, uidbuf, ex); + return _get_message_by_uid (folder, info->info.uid, ex); } - static CamelMimeMessage * _get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex) { - CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder); - GArray *message_info_array; - CamelMboxSummaryInformation *message_info = NULL; - guint32 searched_uid; - int i; - gboolean uid_found; CamelStream *message_stream; CamelMimeMessage *message = NULL; CamelStore *parent_store; + CamelMboxMessageInfo *info; - searched_uid = strtoul (uid, NULL, 10); - - message_info_array = - CAMEL_MBOX_SUMMARY (folder->summary)->message_info; - i=0; - uid_found = FALSE; - - /* first, look for the message that has the searched uid */ - while ((ilen) && (!uid_found)) { - message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i; - uid_found = (message_info->uid == searched_uid); - i++; + /* get the parent store */ + parent_store = camel_folder_get_parent_store (folder, ex); + if (camel_exception_get_id (ex)) { + return NULL; } - - /* if the uid was not found, raise an exception and return */ - if (!uid_found) { + + /* get the message summary info */ + info = camel_mbox_summary_uid(mbox_folder->summary, uid); + + if (info == NULL) { camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, "uid %s not found in the folder", uid); return NULL; } - - /* at this point, the message_info structure - contains the informations concerning the - message that was searched for */ - - /* create a stream bound to the message */ + + /* if this has no content, its an error in the library */ + g_assert(info->info.content); + + /* FIXME: more checks below */ + /* create a stream bound to the message position/size */ message_stream = camel_stream_fs_new_with_name_and_bounds (mbox_folder->folder_file_path, CAMEL_STREAM_FS_READ, - message_info->position, - message_info->position + message_info->size); + ((CamelMboxMessageContentInfo *)info->info.content)->pos, + ((CamelMboxMessageContentInfo *)info->info.content)->endpos); + message = camel_mime_message_new(); + camel_data_wrapper_set_input_stream (CAMEL_DATA_WRAPPER (message), message_stream); + /* init other fields? */ + message->folder = folder; + message->message_uid = g_strdup(uid); - /* get the parent store */ - parent_store = camel_folder_get_parent_store (folder, ex); - if (camel_exception_get_id (ex)) { - gtk_object_unref (GTK_OBJECT (message_stream)); - return NULL; - } - - - message = camel_mime_message_new (); - camel_data_wrapper_set_input_stream (CAMEL_DATA_WRAPPER (message), message_stream); - return message; } + +/* get message info for a range of messages */ +GPtrArray *summary_get_message_info (CamelFolder *folder, int first, int count) +{ + GPtrArray *array = g_ptr_array_new(); + int i, maxcount; + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; + + maxcount = camel_mbox_summary_message_count(mbox_folder->summary); + maxcount = MAX(count, maxcount); + for (i=first;isummary->messages, i)); + + return array; +} diff --git a/camel/providers/mbox/camel-mbox-folder.h b/camel/providers/mbox/camel-mbox-folder.h index 52d3fa6e70..f74c51a6c3 100644 --- a/camel/providers/mbox/camel-mbox-folder.h +++ b/camel/providers/mbox/camel-mbox-folder.h @@ -35,8 +35,8 @@ extern "C" { #include #include "camel-folder.h" -#include "camel-mbox-summary.h" #include "libibex/ibex.h" +#include "camel-mbox-summary.h" /* #include "camel-store.h" */ @@ -45,7 +45,6 @@ extern "C" { #define CAMEL_MBOX_FOLDER_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_MBOX_FOLDER_TYPE, CamelMboxFolderClass)) #define IS_CAMEL_MBOX_FOLDER(o) (GTK_CHECK_TYPE((o), CAMEL_MBOX_FOLDER_TYPE)) - typedef struct { CamelFolder parent_object; @@ -54,11 +53,11 @@ typedef struct { gchar *folder_dir_path; /* contains the subfolders */ gchar *index_file_path; /* index of body contents */ - GList *uid_array; - ibex *index; /* index for this folder */ int search_id; /* next search id */ GList *searches; /* current searches */ + + CamelMboxSummary *summary; } CamelMboxFolder; diff --git a/camel/providers/mbox/camel-mbox-search.c b/camel/providers/mbox/camel-mbox-search.c index 1a584b8db6..1e134476e5 100644 --- a/camel/providers/mbox/camel-mbox-search.c +++ b/camel/providers/mbox/camel-mbox-search.c @@ -84,10 +84,9 @@ struct _searchcontext { ibex *index; /* index of content for this folder */ #endif - CamelFolderSummary *summary; - const GArray *message_info; + CamelMboxSummary *summary; - CamelMessageInfo *message_current; /* when performing a (match operation */ + CamelMboxMessageInfo *message_current; /* when performing a (match operation */ }; struct _glib_sux_donkeys { @@ -115,7 +114,7 @@ func_body_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void if (ctx->index) { for (i=0;itype == ESEXP_RES_STRING) { - truth = ibex_find_name(ctx->index, ctx->message_current->uid, argv[i]->value.string); + truth = ibex_find_name(ctx->index, ctx->message_current->info.uid, argv[i]->value.string); } else { g_warning("Invalid type passed to body-contains match function"); } @@ -189,19 +188,19 @@ func_match_all(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) r = e_sexp_result_new(ESEXP_RES_ARRAY_PTR); r->value.ptrarray = g_ptr_array_new(); - for (i=0;imessage_info->len;i++) { + for (i=0;isummary->messages->len;i++) { if (argc>0) { - ctx->message_current = &g_array_index(ctx->message_info, CamelMessageInfo, i); + ctx->message_current = g_ptr_array_index(ctx->summary->messages, i); r1 = e_sexp_term_eval(f, argv[0]); if (r1->type == ESEXP_RES_BOOL) { if (r1->value.bool) - g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid); + g_ptr_array_add(r->value.ptrarray, ctx->message_current->info.uid); } else { g_warning("invalid syntax, matches require a single bool result"); } e_sexp_result_free(r1); } else { - g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid); + g_ptr_array_add(r->value.ptrarray, ctx->message_current->info.uid); } } ctx->message_current = NULL; @@ -221,27 +220,28 @@ func_header_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, voi /* are we inside a match-all? */ if (ctx->message_current && argc>1 && argv[0]->type == ESEXP_RES_STRING) { - char *headername, *header; + char *headername, *header = NULL; + char strbuf[32]; int i; /* only a subset of headers are supported .. */ headername = argv[0]->value.string; if (!strcasecmp(headername, "subject")) { - header = ctx->message_current->subject; + header = ctx->message_current->info.subject; } else if (!strcasecmp(headername, "date")) { - header = ctx->message_current->sent_date; + sprintf(strbuf, "%d", (int)ctx->message_current->info.date_sent); + header = strbuf; } else if (!strcasecmp(headername, "from")) { - header = ctx->message_current->sender; + header = ctx->message_current->info.from; } else { g_warning("Performing query on unknown header: %s", headername); - header = NULL; } if (header) { for (i=1;itype == ESEXP_RES_STRING && strstr(header, argv[i]->value.string)) { - printf("%s got a match with %s of %s\n", ctx->message_current->uid, header, argv[i]->value.string); + printf("%s got a match with %s of %s\n", ctx->message_current->info.uid, header, argv[i]->value.string); truth = TRUE; break; } @@ -276,6 +276,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr GList *matches = NULL; ESExp *f; ESExpResult *r; + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)folder; /* setup our expression evaluator */ f = e_sexp_new(); @@ -286,7 +287,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr /* setup out context */ ctx->folder = folder; - ctx->summary = camel_folder_get_summary(folder, ex); + ctx->summary = mbox_folder->summary; if (ctx->summary == NULL || camel_exception_get_id (ex)) { printf ("Cannot get summary\n" @@ -296,10 +297,7 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr return -1; } - gtk_object_ref((GtkObject *)ctx->summary); - /* FIXME: the index should be global to the folder */ - ctx->message_info = CAMEL_MBOX_SUMMARY(ctx->summary)->message_info; ctx->message_current = NULL; ctx->index = CAMEL_MBOX_FOLDER(folder)->index; if (!ctx->index) { @@ -337,7 +335,6 @@ int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expr printf("no result!\n"); } - gtk_object_unref((GtkObject *)ctx->summary); gtk_object_unref((GtkObject *)f); i = ctx->id; diff --git a/camel/providers/mbox/camel-mbox-summary.c b/camel/providers/mbox/camel-mbox-summary.c index e15bb13b03..11f1c5779a 100644 --- a/camel/providers/mbox/camel-mbox-summary.c +++ b/camel/providers/mbox/camel-mbox-summary.c @@ -1,420 +1,1258 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* - * Author : Bertrand Guiheneuf +/* + * Copyright (C) 2000 Helix Code Inc. * - * Copyright (C) 1999 - 2000 Helix Code . - - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. + * Authors: Michael Zucchi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include - -#include "camel-exception.h" -#include "camel-mbox-folder.h" -#include "camel-mbox-summary.h" -#include "md5-utils.h" - - -#include -#include #include +#include #include -#include +#include +#include + #include #include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "camel-mbox-summary.h" + #include +#include #include -static CamelFolderSummaryClass *parent_class = NULL; +#define d(x) -static int count_messages (CamelFolderSummary *summary); -static int count_subfolders (CamelFolderSummary *summary); -static GPtrArray *get_subfolder_info (CamelFolderSummary *summary, - int first, int count); -static GPtrArray *get_message_info (CamelFolderSummary *summary, - int first, int count); -static void finalize (GtkObject *object); +#define CAMEL_MBOX_SUMMARY_VERSION 2 -static void -camel_mbox_summary_class_init (CamelMboxSummaryClass *camel_mbox_summary_class) +static int safe_write(int fd, char *buffer, size_t towrite); +static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info); + +/* + Disk file format? + + message uid +message-block + date: + date received? + + subject: (unicode encoded) + from: (unicode encoded) + to: (unicode) + + content-block + +content-block + content-type: ; params; + content-id: + content-description: + content-transfer-encoding: + message-start: + header-size: + body-size: + + message-block + multipart-block + + */ + +/* pah, i dont care, its almost no code and it works, dont need a glist */ +struct _node { + struct _node *next; +}; + +static struct _node * +my_list_append(struct _node **list, struct _node *n) +{ + struct _node *ln = (struct _node *)list; + while (ln->next) + ln = ln->next; + n->next = 0; + ln->next = n; + return n; +} + +static int +my_list_size(struct _node **list) +{ + int len = 0; + struct _node *ln = (struct _node *)list; + while (ln->next) { + ln = ln->next; + len++; + } + return len; +} + +/* low-level io functions */ +static int +encode_int (FILE *out, gint32 value) +{ + int i; + + for (i=28;i>0;i-=7) { + if (value >= (1<>i) & 0x7f; + if (fputc(c, out) == -1) + return -1; + } + } + return fputc(value | 0x80, out); +} + +static gint32 +decode_int (FILE *in) { - GtkObjectClass *gtk_object_class = - GTK_OBJECT_CLASS (camel_mbox_summary_class); - CamelFolderSummaryClass *camel_folder_summary_class = - CAMEL_FOLDER_SUMMARY_CLASS (camel_mbox_summary_class); + gint32 value=0, v; - parent_class = gtk_type_class (camel_folder_summary_get_type ()); + /* until we get the last byte, keep decoding 7 bits at a time */ + while ( ((v = fgetc(in)) & 0x80) == 0 && v!=EOF) { + value |= v; + value <<= 7; + } + value |= (v&0x7f); + return value; +} - /* virtual method override */ - camel_folder_summary_class->count_messages = count_messages; - camel_folder_summary_class->count_subfolders = count_subfolders; - camel_folder_summary_class->get_subfolder_info = get_subfolder_info; - camel_folder_summary_class->get_message_info = get_message_info; +static int +encode_fixed_int (FILE *out, gint32 value) +{ + guint32 save; - gtk_object_class->finalize = finalize; + save = htonl(value); + return fwrite(&save, sizeof(save), 1, out); } +static gint32 +decode_fixed_int (FILE *out) +{ + guint32 save; -GtkType -camel_mbox_summary_get_type (void) + if (fread(&save, sizeof(save), 1, out) != -1) { + return ntohl(save); + } else { + return -1; + } +} + +/* should be sorted, for binary search */ +/* This is a tokenisation mechanism for strings written to the + summary - to save space. + This list can have at most 31 words. */ +static char * tokens[] = { + "7bit", + "8bit", + "alternative", + "application", + "base64", + "boundary", + "charset", + "filename", + "html", + "image", + "iso-8859-1", + "iso-8859-8", + "message", + "mixed", + "multipart", + "name", + "octet-stream", + "parallel", + "plain", + "quoted-printable", + "rfc822", + "text", + "us-ascii", /* 23 words */ +}; + +#define tokens_len (sizeof(tokens)/sizeof(tokens[0])) + +/* baiscally ... + 0 = null + 1-tokens_len == tokens[id-1] + >=32 string, length = n-32 +*/ + +static int +encode_string (FILE *out, char *str) { - static GtkType camel_mbox_summary_type = 0; + if (str == NULL) { + return encode_int(out, 0); + } else { + int len = strlen(str); + int i, token=-1; + + if (len <= 16) { + char lower[32]; + + for (i=0;i 10240) { + g_warning("Got broken string header length: %d bytes", len); + ret = NULL; + } else { + len -= 32; + ret = g_malloc(len+1); + if (fread(ret, len, 1, in) == -1) { + g_free(ret); + return NULL; + } + ret[len]=0; } - return camel_mbox_summary_type; + return ret; } + + +/* allocation functions */ + static void -finalize (GtkObject *object) +body_part_dump(CamelMboxMessageContentInfo *bs, int depth) { - CamelMboxSummary *summary = CAMEL_MBOX_SUMMARY (object); - CamelMboxSummaryInformation *info; - int i; + CamelMboxMessageContentInfo *c; + char *prefix; - for (i = 0; i < summary->message_info->len; i++) { - info = &(((CamelMboxSummaryInformation *)summary->message_info->data)[i]); - g_free (info->headers.subject); - g_free (info->headers.sender); - g_free (info->headers.to); - g_free (info->headers.sent_date); - g_free (info->headers.received_date); - g_free (info->headers.uid); + if (bs == NULL) + return; + + prefix = alloca(depth*2+1); + memset(prefix, ' ', depth*2); + prefix[depth*2]=0; + printf("%scontent-range: %d %d %d\n", prefix, (int)bs->pos, (int)bs->bodypos, (int)bs->endpos); + printf("%scontent-type: %s/%s\n", prefix, bs->info.type?bs->info.type->type:"?", bs->info.type?bs->info.type->subtype:"?"); + printf("%scontent-id: %s\n", prefix, bs->info.id); + printf("%scontent-description: %s\n", prefix, bs->info.description); + printf("%scontent-transfer-encoding: %s\n", prefix, bs->info.encoding); + c = (CamelMboxMessageContentInfo *)bs->info.childs; + while (c) { + printf("%s -- \n", prefix); + body_part_dump(c, depth+1); + c = (CamelMboxMessageContentInfo *)c->info.next; } - g_array_free (summary->message_info, TRUE); +} - GTK_OBJECT_CLASS (parent_class)->finalize (object); -} +static void +message_struct_dump(CamelMboxMessageInfo *ms) +{ + char *tmp; -static int -count_messages (CamelFolderSummary *summary) + if (ms == NULL) { + printf("Empty message?\n"); + return; + } + + printf("Subject: %s\n", ms->info.subject); + printf("From: %s\n", ms->info.from); + printf("To: %s\n", ms->info.to); + tmp = header_format_date(ms->info.date_sent, 0); + printf("Date: %s\n", tmp); + g_free(tmp); + tmp = header_format_date(ms->info.date_received, 0); + printf("Date-Received: %s\n", tmp); + g_free(tmp); + printf("UID: %08x-%04x\n", atoi(ms->info.uid), ms->info.flags); + printf(" -- content ->\n"); + body_part_dump((CamelMboxMessageContentInfo *)ms->info.content, 1); +} + +static CamelMboxMessageContentInfo * +body_part_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body) { - return CAMEL_MBOX_SUMMARY (summary)->nb_message; + CamelMboxMessageContentInfo *bs; + + bs = g_malloc0(sizeof(*bs)); + + bs->info.parent = (CamelMessageContentInfo *)parent; + + bs->info.type = camel_mime_parser_content_type(mp); + header_content_type_ref(bs->info.type); + + bs->info.id = header_msgid_decode(camel_mime_parser_header(mp, "content-id", NULL)); + bs->info.description = header_decode_string(camel_mime_parser_header(mp, "content-description", NULL)); + bs->info.encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); + + /* not sure what to set here? */ + bs->pos = start; + bs->bodypos = body; + bs->endpos = -1; + + if (parent) + my_list_append((struct _node **)&parent->info.childs, (struct _node *)bs); + + return bs; +} + +static CamelMboxMessageInfo * +message_struct_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body, off_t xev_offset) +{ + CamelMboxMessageInfo *ms; + + ms = g_malloc0(sizeof(*ms)); + + /* FIXME: what about cc, sender vs from? */ + ms->info.subject = g_strdup(camel_mime_parser_header(mp, "subject", NULL)); + ms->info.from = g_strdup(camel_mime_parser_header(mp, "from", NULL)); + ms->info.to = g_strdup(camel_mime_parser_header(mp, "to", NULL)); + + ms->info.date_sent = header_decode_date(camel_mime_parser_header(mp, "date", NULL), NULL); + ms->info.date_received = 0; + + ms->info.content = (CamelMessageContentInfo *)body_part_new(mp, parent, start, body); + ms->xev_offset = xev_offset; + return ms; +} + +static void +body_part_free(CamelMboxMessageContentInfo *bs) +{ + CamelMboxMessageContentInfo *c, *cn; + + c = (CamelMboxMessageContentInfo *)bs->info.childs; + while (c) { + cn = (CamelMboxMessageContentInfo *)c->info.next; + body_part_free(c); + c = cn; + } + g_free(bs->info.id); + g_free(bs->info.description); + g_free(bs->info.encoding); + header_content_type_unref(bs->info.type); + g_free(bs); +} + +static void +message_struct_free(CamelMboxMessageInfo *ms) +{ + g_free(ms->info.subject); + g_free(ms->info.to); + g_free(ms->info.from); + body_part_free((CamelMboxMessageContentInfo *)ms->info.content); + g_free(ms); +} + + +/* IO functions */ +static CamelMboxMessageContentInfo * +body_part_load(FILE *in) +{ + CamelMboxMessageContentInfo *bs = NULL, *c; + struct _header_content_type *ct; + char *type; + char *subtype; + int i, count; + + d(printf("got content-block\n")); + bs = g_malloc0(sizeof(*bs)); + bs->pos = decode_int(in); + bs->bodypos = bs->pos + decode_int(in); + bs->endpos = bs->pos + decode_int(in); + + /* do content type */ + d(printf("got content-type\n")); + type = decode_string(in); + subtype = decode_string(in); + + ct = header_content_type_new(type, subtype); + bs->info.type = ct; + count = decode_int(in); + d(printf("getting %d params\n", count)); + for (i=0;iinfo.id = decode_string(in); + d(printf("got content-description\n")); + bs->info.description = decode_string(in); + d(printf("got content-encoding\n")); + bs->info.encoding = decode_string(in); + + count = decode_int(in); + d(printf("got children, %d\n", count)); + for (i=0;iinfo.childs, (struct _node *)c); + c->info.parent = (CamelMessageContentInfo *)bs; + } else { + printf("Cannot load child\n"); + } + } + + return bs; } static int -count_subfolders (CamelFolderSummary *summary) +body_part_save(FILE *out, CamelMboxMessageContentInfo *bs) { - /* XXX */ - g_warning ("CamelMboxSummary::count_subfolders not implemented"); + CamelMboxMessageContentInfo *c, *cn; + struct _header_content_type *ct; + struct _header_param *hp; + + encode_int(out, bs->pos); + encode_int(out, bs->bodypos - bs->pos); + encode_int(out, bs->endpos - bs->pos); + + ct = bs->info.type; + if (ct) { + encode_string(out, ct->type); + encode_string(out, ct->subtype); + encode_int(out, my_list_size((struct _node **)&ct->params)); + hp = ct->params; + while (hp) { + encode_string(out, hp->name); + encode_string(out, hp->value); + hp = hp->next; + } + } else { + encode_string(out, NULL); + encode_string(out, NULL); + encode_int(out, 0); + } + encode_string(out, bs->info.id); + encode_string(out, bs->info.description); + encode_string(out, bs->info.encoding); + + encode_int(out, my_list_size((struct _node **)&bs->info.childs)); + + c = (CamelMboxMessageContentInfo *)bs->info.childs; + while (c) { + cn = (CamelMboxMessageContentInfo *)c->info.next; + body_part_save(out, c); + c = cn; + } + return 0; } -static GPtrArray * -get_subfolder_info (CamelFolderSummary *summary, int first, int count) +static CamelMboxMessageInfo * +message_struct_load(FILE *in) { - /* XXX */ - g_warning ("CamelMboxSummary::count_subfolders not implemented"); + CamelMboxMessageInfo *ms; + + ms = g_malloc0(sizeof(*ms)); + + ms->info.uid = g_strdup_printf("%u", decode_int(in)); + ms->info.flags = decode_int(in); + ms->info.date_sent = decode_int(in); + ms->info.date_received = decode_int(in); + ms->xev_offset = decode_int(in); + ms->info.subject = decode_string(in); + ms->info.from = decode_string(in); + ms->info.to = decode_string(in); + ms->info.content = (CamelMessageContentInfo *)body_part_load(in); + + return ms; +} + +static int +message_struct_save(FILE *out, CamelMboxMessageInfo *ms) +{ + encode_int(out, strtoul(ms->info.uid, NULL, 10)); + encode_int(out, ms->info.flags); + encode_int(out, ms->info.date_sent); + encode_int(out, ms->info.date_received); + encode_int(out, ms->xev_offset); + encode_string(out, ms->info.subject); + encode_string(out, ms->info.from); + encode_string(out, ms->info.to); + body_part_save(out, (CamelMboxMessageContentInfo *)ms->info.content); + return 0; } -static GPtrArray * -get_message_info (CamelFolderSummary *summary, int first, int count) +static unsigned int +header_evolution_decode(const char *in, unsigned int *uid, unsigned int *flags) { - CamelMboxSummary *mbox_summary = CAMEL_MBOX_SUMMARY (summary); - CamelMboxSummaryInformation *info; - GPtrArray *arr; + char *header; + if (in + && (header = header_decode_token(&in))) { + if (strlen(header) == strlen("00000000-0000") + && sscanf(header, "%08x-%04x", uid, flags) == 2) { + g_free(header); + return *uid; + } + g_free(header); + } - /* XXX bounds check */ + return ~0; +} + +static int +safe_write(int fd, char *buffer, size_t towrite) +{ + size_t donelen; + size_t len; - arr = g_ptr_array_new (); - for (; count; count--) { - info = &((CamelMboxSummaryInformation *)mbox_summary->message_info->data)[first++]; - g_ptr_array_add (arr, info); + donelen = 0; + while (donelen < towrite) { + len = write(fd, buffer + donelen, towrite - donelen); + if (len == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + donelen += len; } + return donelen; +} - return arr; -} - -/** - * camel_mbox_summary_save: - * @summary: - * @filename: - * @ex: - * - * save the summary into a file - **/ -void -camel_mbox_summary_save (CamelMboxSummary *summary, const gchar *filename, - CamelException *ex) -{ - CamelMboxSummaryInformation *msg_info; - guint cur_msg; - guint field_length; - gint fd; - gint write_result; /* XXX use this */ - guint32 data; - - fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - if (fd == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, - "could not create the mbox summary " - "file\n\t%s\nFull error is : %s\n", - filename, - strerror (errno)); - return; +static int +header_write(int fd, struct _header_raw *header, unsigned int uid, unsigned int flags) +{ + struct iovec iv[3]; + int outlen = 0; + + iv[1].iov_base = ":"; + iv[1].iov_len = 1; + + while (header) { + if (strcasecmp(header->name, "x-evolution")) { + int len; + + iv[0].iov_base = header->name; + iv[0].iov_len = strlen(header->name); + iv[2].iov_base = header->value; + iv[2].iov_len = strlen(header->value); + + do { + len = writev(fd, iv, 3); + } while (len == -1 && errno == EINTR); + + if (len == -1) + return -1; + outlen += len; + } + header = header->next; } - /* We write the file out in network byte order, not because - * that makes sense, but because it's easy. - */ - - data = htonl (CAMEL_MBOX_SUMMARY_VERSION); - write (fd, &data, sizeof (data)); - - data = htonl (summary->nb_message); - write (fd, &data, sizeof (data)); - data = htonl (summary->next_uid); - write (fd, &data, sizeof (data)); - data = htonl (summary->mbox_file_size); - write (fd, &data, sizeof (data)); - data = htonl (summary->mbox_modtime); - write (fd, &data, sizeof (data)); - - for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) { - msg_info = (CamelMboxSummaryInformation *) - (summary->message_info->data) + cur_msg; - - /* Write meta-info. */ - data = htonl (msg_info->position); - write (fd, &data, sizeof (data)); - data = htonl (msg_info->size); - write (fd, &data, sizeof (data)); - data = htonl (msg_info->x_evolution_offset); - write (fd, &data, sizeof (data)); - data = htonl (msg_info->uid); - write (fd, &data, sizeof (data)); - write (fd, &msg_info->status, 1); - - /* Write subject. */ - if (msg_info->headers.subject) - field_length = strlen (msg_info->headers.subject); - else - field_length = 0; - data = htonl (field_length); - write (fd, &data, sizeof (data)); - if (msg_info->headers.subject) - write (fd, msg_info->headers.subject, field_length); - - /* Write sender. */ - if (msg_info->headers.sender) - field_length = strlen (msg_info->headers.sender); - else - field_length = 0; - data = htonl (field_length); - write (fd, &data, sizeof (data)); - if (msg_info->headers.sender) - write (fd, msg_info->headers.sender, field_length); - - /* Write to. */ - if (msg_info->headers.to) - field_length = strlen (msg_info->headers.to); - else - field_length = 0; - data = htonl (field_length); - write (fd, &data, sizeof (data)); - if (msg_info->headers.to) - write (fd, msg_info->headers.to, field_length); - - /* Write sent date. */ - if (msg_info->headers.sent_date) - field_length = strlen (msg_info->headers.sent_date); - else - field_length = 0; - data = htonl (field_length); - write (fd, &data, sizeof (data)); - if (msg_info->headers.sent_date) - write (fd, msg_info->headers.sent_date, field_length); - - /* Write received date. */ - if (msg_info->headers.received_date) - field_length = strlen (msg_info->headers.received_date); + return outlen; +} + +/* returns -1 on error, else number of bytes written */ +int +camel_mbox_summary_copy_block(int fromfd, int tofd, off_t readpos, size_t bytes) +{ + char buffer[4096]; + int written = 0; + off_t pos, newpos; + + pos = lseek(fromfd, 0, SEEK_CUR); + if (pos == -1) + return -1; + + newpos = lseek(fromfd, readpos, SEEK_SET); + if (newpos == -1 || newpos != readpos) + goto error; + + d(printf("oldpos = %d; copying %d from %d\n", (int)pos, (int)bytes, (int)readpos)); + + while (bytes>0) { + int toread, towrite, donelen; + + toread = bytes; + if (bytes>4096) + toread = 4096; else - field_length = 0; - data = htonl (field_length); - write (fd, &data, sizeof (data)); - if (msg_info->headers.received_date) - write (fd, msg_info->headers.received_date, field_length); + toread = bytes; + reread: + towrite = read(fromfd, buffer, toread); + if (towrite == -1) { + if (errno == EINTR || errno == EAGAIN) + goto reread; + goto error; + } + + /* check for 'end of file' */ + if (towrite == 0) + break; + + if ( (donelen = safe_write(tofd, buffer, towrite)) == -1) + goto error; + + written += donelen; + bytes -= donelen; + } + + d(printf("written %d bytes\n", written)); + + newpos = lseek(fromfd, pos, SEEK_SET); + if (newpos == -1 || newpos != pos); + return -1; + + return written; + +error: + lseek(fromfd, pos, SEEK_SET); + return -1; +} + +#define SAVEIT + +static int index_folder(CamelMboxSummary *s, int startoffset) +{ + CamelMimeParser *mp; + int fd; + int fdout; + int state; + + int toplevel = FALSE; + const char *xev; + char *data; + int datalen; + + int enc_id=-1; + int chr_id=-1; + int idx_id=-1; + struct _header_content_type *ct; + int doindex=FALSE; + CamelMimeFilterCharset *mfc = NULL; + CamelMimeFilterIndex *mfi = NULL; + CamelMimeFilterBasic *mf64 = NULL, *mfqp = NULL; + + CamelMboxMessageContentInfo *body = NULL, *parent = NULL; + CamelMboxMessageInfo *message = NULL; + + int from_end = 0; /* start of message */ + int from = 0; /* start of headers */ + int last_write = 0; /* last written position */ + int eof; + int write_offset = 0; /* how much does the dest differ from the source pos */ + int old_offset = 0; + + guint32 newuid; + off_t xevoffset = -1; + + char *tmpname; + + printf("indexing %s (%s) from %d\n", s->folder_path, s->summary_path, startoffset); + + fd = open(s->folder_path, O_RDONLY); + if (fd==-1) { + perror("Can't open folder"); + return -1; } - close (fd); -} - - - -/** - * camel_mbox_summary_load: - * @filename: - * @ex: - * - * load the summary from a file - * - * Return value: - **/ -CamelMboxSummary * -camel_mbox_summary_load (const gchar *filename, CamelException *ex) -{ - CamelMboxSummaryInformation *msg_info; - guint cur_msg; - guint field_length; - gint fd; - CamelMboxSummary *summary; - gint read_result; - guint32 data; - - fd = open (filename, O_RDONLY); - if (fd == -1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, - "could not open the mbox summary file\n" - "\t%s\nFull error is : %s\n", - filename, strerror (errno)); - return NULL; + tmpname = g_strdup_printf("%s.tmp", s->folder_path); + + fdout = open(tmpname, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fdout==-1) { + perror("Can't open output"); + g_free(tmpname); + return -1; } - /* Verify version number. */ - read (fd, &data, sizeof(data)); - data = ntohl (data); - - if (data != CAMEL_MBOX_SUMMARY_VERSION) { - camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID, - "This folder summary was written by " - "%s version of this software.", - data < CAMEL_MBOX_SUMMARY_VERSION ? - "an older" : "a newer"); - return NULL; + mp = camel_mime_parser_new(); + camel_mime_parser_init_with_fd(mp, fd); + camel_mime_parser_scan_from(mp, TRUE); + + /* FIXME: cleaner fail code */ + if (startoffset > 0) { + if (camel_mime_parser_seek(mp, startoffset, SEEK_SET) != startoffset) { + g_free(tmpname); + gtk_object_unref((GtkObject *)mp); + return -1; + } } - summary = CAMEL_MBOX_SUMMARY (gtk_object_new (camel_mbox_summary_get_type (), NULL)); - - read (fd, &data, sizeof(data)); - summary->nb_message = ntohl (data); - read (fd, &data, sizeof(data)); - summary->next_uid = ntohl (data); - read (fd, &data, sizeof(data)); - summary->mbox_file_size = ntohl (data); - read (fd, &data, sizeof(data)); - summary->mbox_modtime = ntohl (data); - - summary->message_info = - g_array_new (FALSE, FALSE, - sizeof (CamelMboxSummaryInformation)); - g_array_set_size (summary->message_info, summary->nb_message); - - for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) { - msg_info = (CamelMboxSummaryInformation *) - (summary->message_info->data) + cur_msg; - - /* Read the meta-info. */ - read (fd, &data, sizeof(data)); - msg_info->position = ntohl (data); - read (fd, &data, sizeof(data)); - msg_info->size = ntohl (data); - read (fd, &data, sizeof(data)); - msg_info->x_evolution_offset = ntohl (data); - read (fd, &data, sizeof(data)); - msg_info->uid = ntohl (data); - msg_info->headers.uid = g_strdup_printf ("%d", msg_info->uid); - read (fd, &msg_info->status, 1); - - /* Read the subject. */ - read (fd, &field_length, sizeof (field_length)); - field_length = ntohl (field_length); - if (field_length > 0) { - msg_info->headers.subject = - g_new0 (gchar, field_length + 1); - read (fd, msg_info->headers.subject, field_length); - } else - msg_info->headers.subject = NULL; - - /* Read the sender. */ - read (fd, &field_length, sizeof (field_length)); - field_length = ntohl (field_length); - if (field_length > 0) { - msg_info->headers.sender = - g_new0 (gchar, field_length + 1); - read (fd, msg_info->headers.sender, field_length); - } else - msg_info->headers.sender = NULL; - - /* Read the "to" field. */ - read (fd, &field_length, sizeof (field_length)); - field_length = ntohl (field_length); - if (field_length > 0) { - msg_info->headers.to = - g_new0 (gchar, field_length + 1); - read (fd, msg_info->headers.to, field_length); - } else - msg_info->headers.to = NULL; - - /* Read the sent date field. */ - read (fd, &field_length, sizeof (field_length)); - field_length = ntohl (field_length); - if (field_length > 0) { - msg_info->headers.sent_date = - g_new0 (gchar, field_length + 1); - read (fd, msg_info->headers.sent_date, field_length); - } else - msg_info->headers.sent_date = NULL; - - /* Read the received date field. */ - read (fd, &field_length, sizeof (field_length)); - field_length = ntohl (field_length); - if (field_length > 0) { - msg_info->headers.received_date = - g_new0 (gchar, field_length + 1); - read (fd, msg_info->headers.received_date, - field_length); - } else - msg_info->headers.received_date = NULL; - } - - close (fd); - return summary; -} - - -/** - * camel_mbox_summary_append_entries: - * @summary: - * @entries: - * - * append an entry to a summary - **/ -void -camel_mbox_summary_append_entries (CamelMboxSummary *summary, GArray *entries) -{ - - summary->message_info = g_array_append_vals (summary->message_info, - entries->data, - entries->len); + mfi = camel_mime_filter_index_new_ibex(s->index); + + while ( (state = camel_mime_parser_step(mp, &data, &datalen)) != HSCAN_EOF ) { + switch(state) { + case HSCAN_FROM: /* starting a new message content */ + /* save the current position */ + d(printf("from = %d\n", (int)camel_mime_parser_tell(mp))); + toplevel = FALSE; + from = camel_mime_parser_tell(mp); + break; + + case HSCAN_FROM_END: + d(printf("from-end = %d\n", (int)camel_mime_parser_tell(mp))); + d(printf("message from %d to %d\n", from_end, (int)camel_mime_parser_tell(mp))); + from_end = camel_mime_parser_tell(mp); + break; + + case HSCAN_MESSAGE: + case HSCAN_MULTIPART: + case HSCAN_HEADER: /* starting a new header */ + newuid=~0; + if (!toplevel) { + char name[32]; + unsigned int olduid, oldflags; + int headerlen; + int docopy = FALSE; + + /* check for X-Evolution header ... if its there, nothing to do (skip content) */ + xev = camel_mime_parser_header(mp, "x-evolution", &xevoffset); + if (xev) { + d(printf("An x-evolution header exists at: %d = %s\n", xevoffset + write_offset, xev)); + xevoffset = xevoffset + write_offset; + if (header_evolution_decode(xev, &olduid, &oldflags) != ~0) { + d(printf(" uid = %d = %x\n", olduid, olduid)); + newuid = olduid; +#if 0 + while (camel_mime_parser_step(mp, &data, &datalen) != HSCAN_FROM_END) + ; + break; +#endif + } else { + printf("Invalid xev header? I need to write out a new one ...\n"); + } + } + + toplevel = TRUE; + + /* assign a new uid for this message */ + if (newuid == ~0) { + newuid = s->nextuid++; + docopy = TRUE; + } else { + /* make sure we account for this uid when assigning uid's */ + /* this really needs a pre-scan pass ... *sigh* */ + camel_mbox_summary_set_uid(s, newuid); + } + + /* setup index name for this uid */ + sprintf(name, "%x", newuid); + camel_mime_filter_index_set_name(mfi, name); + /* remove all references to this name from the index */ + if (s->index) + ibex_unindex(s->index, name); + + d(printf("Message content starts at %d\n", camel_mime_parser_tell(mp))); + + if (docopy) { + /* now, copy over bits of mbox from last write, and insert the X-Evolution header (at the top of headers) */ + /* if we already have a valid x-evolution header, use that, dont need to copy */ + camel_mbox_summary_copy_block(fd, fdout, last_write, from-last_write); + last_write = from; + + headerlen = header_write(fdout, camel_mime_parser_headers_raw(mp), newuid, 0); + sprintf(name, "X-Evolution: %08x-%04x\n\n", newuid, 0); + safe_write(fdout, name, strlen(name)); + d(printf("new X-Evolution at %d\n", headerlen + from + write_offset)); + xevoffset = headerlen + from + write_offset; + old_offset = write_offset; + + write_offset += (headerlen - (camel_mime_parser_tell(mp)-from)) + strlen(name); + last_write = camel_mime_parser_tell(mp); + } + } else { + old_offset = write_offset; + } + + /* we only care about the rest for actual content parts */ + /* TODO: Cleanup, this is a huge mess */ + if (state != HSCAN_HEADER) { + if (message == NULL) { + message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset); + parent = (CamelMboxMessageContentInfo *)message->info.content; + if (newuid != ~0) { + message->info.uid = g_strdup_printf("%u", newuid); + } else { + g_warning("This shouldn't happen?"); + } + } else { + parent = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset); + } + break; + } + + if (message == NULL) { + message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset); + body = (CamelMboxMessageContentInfo *)message->info.content; + if (newuid != ~0) { + message->info.uid = g_strdup_printf("%u", newuid); + } else { + g_warning("This shouldn't happen?"); + } + } else { + body = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset); + } + + /* check headers for types that we can index */ + ct = camel_mime_parser_content_type(mp); + if (header_content_type_is(ct, "text", "*")) { + char *encoding; + const char *charset; + + /* TODO: The filters should all be cached, so they aren't recreated between + messages/message parts */ + encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); + if (encoding) { + if (!strcasecmp(encoding, "base64")) { + d(printf("Adding decoding filter for base64\n")); + if (mf64 == NULL) + mf64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC); + enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mf64); + } else if (!strcasecmp(encoding, "quoted-printable")) { + d(printf("Adding decoding filter for quoted-printable\n")); + if (mfqp == NULL) + mfqp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC); + enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfqp); + } + g_free(encoding); + } + + charset = header_content_type_param(ct, "charset"); + if (charset!=NULL + && !(strcasecmp(charset, "us-ascii")==0 + || strcasecmp(charset, "utf-8")==0)) { + d(printf("Adding conversion filter from %s to utf-8\n", charset)); + if (mfc == NULL) + mfc = camel_mime_filter_charset_new_convert(charset, "utf-8"); + if (mfc) { + chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc); + } else { + g_warning("Cannot convert '%s' to 'utf-8', message display may be corrupt", charset); + } + } + + doindex = TRUE; + + /* and this filter actually does the indexing */ + idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfi); + } else { + doindex = FALSE; + } + break; + + /* fixme, this needs thought *sigh* */ + case HSCAN_MESSAGE_END: + case HSCAN_MULTIPART_END: + if (parent) { + parent->endpos = camel_mime_parser_tell(mp)+write_offset; + if (parent->info.parent == NULL) { + camel_mbox_summary_add(s, message); + message = NULL; + parent = NULL; + } else { + parent = (CamelMboxMessageContentInfo *)parent->info.parent; + } + } + break; + + case HSCAN_BODY: + if (doindex) { + d(printf("Got content to index:\n%.*s", datalen, data)); + } + break; + + case HSCAN_BODY_END: + if (body) { + body->endpos = camel_mime_parser_tell(mp)+write_offset; + if (body->info.parent == NULL) { + camel_mbox_summary_add(s, message); + message = NULL; + } + } + + d(printf("end of content, removing decoders\n")); + if (enc_id != -1) { + camel_mime_parser_filter_remove(mp, enc_id); + enc_id = -1; + } + if (chr_id != -1) { + camel_mime_parser_filter_remove(mp, chr_id); + chr_id = -1; + } + if (idx_id != -1) { + camel_mime_parser_filter_remove(mp, idx_id); + idx_id = -1; + } + break; + } + } + + /* did we actually write anything out? Then rename and be done with it. */ + if (last_write>0) { + eof = camel_mime_parser_tell(mp); + camel_mbox_summary_copy_block(fd, fdout, last_write, eof-last_write); + + if (close(fdout) == -1) { + perror("Could not close output file"); + unlink(tmpname); + } else { + printf("renaming %s to %s\n", tmpname, s->folder_path); + if (rename(tmpname, s->folder_path) == -1) { + perror("Error renaming file"); + unlink(tmpname); + } + } + } else { + /* no, then dont bother touching the inbox */ + printf("No written changes to mbox, removing tmp file\n"); + close(fdout); + unlink(tmpname); + } + + close(fd); + + if (mf64) gtk_object_unref((GtkObject *)mf64); + if (mfqp) gtk_object_unref((GtkObject *)mfqp); + if (mfc) gtk_object_unref((GtkObject *)mfc); + if (mfi) gtk_object_unref((GtkObject *)mfi); + + /* force an index sync? */ + if (s->index) { + ibex_write(s->index); + } + + gtk_object_unref((GtkObject *)mp); + + /* and finally ... update the summary sync info */ + { + struct stat st; + + if (stat(s->folder_path, &st) == 0) { + s->time = st.st_mtime; + s->size = st.st_size; + } + } + + g_free(tmpname); + + return 0; +} + +CamelMboxSummary *camel_mbox_summary_new(const char *summary, const char *folder, ibex *index) +{ + CamelMboxSummary *s; + + s = g_malloc0(sizeof(*s)); + + s->dirty = TRUE; + s->folder_path = g_strdup(folder); + s->summary_path = g_strdup(summary); + /* FIXME: refcount index? */ + s->index = index; + + s->messages = g_ptr_array_new(); + s->message_uid = g_hash_table_new(g_str_hash, g_str_equal); + + /* always force an update */ + s->time = 0; + s->size = 0; + + s->nextuid = 1; + + /* TODO: force an initial load right now? */ + + return s; +} + +void camel_mbox_summary_unref(CamelMboxSummary *s) +{ + g_warning("Unimplemented function, mbox_summary_unref"); +} + +/* check that the summary is uptodate, TRUE means it is uptodate */ +int camel_mbox_summary_check(CamelMboxSummary *s) +{ + struct stat st; + + /* no folder at all? */ + if (stat(s->folder_path, &st) != 0) + return FALSE; + + return (st.st_size == s->size) && (st.st_mtime == s->time); +} + +static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info) +{ + if (info->info.uid == NULL) { + info->info.uid = g_strdup_printf("%u", s->nextuid++); + } + if (g_hash_table_lookup(s->message_uid, info->info.uid)) { + g_error("Trying to insert message with clashing uid's"); + } + d(printf("adding %s\n", info->info.uid)); + g_ptr_array_add(s->messages, info); + g_hash_table_insert(s->message_uid, info->info.uid, info); + s->dirty = TRUE; +} + +static int summary_header_read(FILE *fp, guint32 *version, time_t *time, size_t *size, guint32 *nextuid) +{ + fseek(fp, 0, SEEK_SET); + *version = decode_fixed_int(fp); + *time = decode_fixed_int(fp); + *size = decode_fixed_int(fp); + *nextuid = decode_fixed_int(fp); + return ferror(fp); +} + +static void +summary_clear(CamelMboxSummary *s) +{ + int i; + + for (i=0;imessages->len;i++) { + message_struct_free(g_ptr_array_index(s->messages, i)); + } + g_ptr_array_free(s->messages, TRUE); + g_hash_table_destroy(s->message_uid); + + s->messages = g_ptr_array_new(); + s->message_uid = g_hash_table_new(g_str_hash, g_str_equal); +} + +int camel_mbox_summary_load(CamelMboxSummary *s) +{ + struct stat st; + FILE *fp; + int i, total; + CamelMboxMessageInfo *info; + + summary_clear(s); + + if ((fp = fopen(s->summary_path, "r")) == NULL) { + g_warning("Loading non-existant summary, generating summary for folder: %s: %s", s->summary_path, strerror(errno)); + index_folder(s, 0); + camel_mbox_summary_save(s); + } else { + guint32 version, nextuid; + time_t time; + size_t size; + + if (stat(s->folder_path, &st) != 0) { + g_warning("Uh, no folder anyway, aborting"); + fclose(fp); + return -1; + } + + if (summary_header_read(fp, &version, &time, &size, &nextuid) != 0 + || version != CAMEL_MBOX_SUMMARY_VERSION) { + g_warning("Summary missing or version mismatch, reloading summary"); + fclose(fp); + index_folder(s, 0); + camel_mbox_summary_save(s); + return 0; + } + + s->nextuid = MAX(s->nextuid, nextuid); + s->time = time; + s->size = size; + total = decode_fixed_int(fp); + + if (time != st.st_mtime || size != st.st_size) { + /* if its grown, then just index the new stuff, and load the rest from the summary */ + if (size < st.st_size) { + g_warning("Indexing/summarizing from start position: %d", size); + + d(printf("loading %d items from summary file\n", total)); + for (i=0;idirty = FALSE; + index_folder(s, size); /* if it adds any, it'll dirtify it */ + camel_mbox_summary_save(s); + } else { + g_warning("Folder changed/smaller, reindexing everything"); + index_folder(s, 0); + camel_mbox_summary_save(s); + fclose(fp); + } + return 0; + } + + printf("loading %d items from summary file\n", total); + for (i=0;idirty = FALSE; + } + return 0; +} + +static int summary_header_write(FILE *fp, CamelMboxSummary *s) +{ + fseek(fp, 0, SEEK_SET); + encode_fixed_int(fp, CAMEL_MBOX_SUMMARY_VERSION); + encode_fixed_int(fp, s->time); + /* if we're dirty, then dont *really* save it ... */ + if (s->dirty) + encode_fixed_int(fp, 0); + else + encode_fixed_int(fp, s->size); + encode_fixed_int(fp, s->nextuid); + fflush(fp); + return ferror(fp); +} + +static int summary_header_save(CamelMboxSummary *s) +{ + int fd; + FILE *fp; + + fd = open(s->summary_path, O_WRONLY|O_CREAT, 0600); + if (fd == -1) + return -1; + fp = fdopen(fd, "w"); + if (fp == NULL) + return -1; + + summary_header_write(fp, s); + return fclose(fp); +} + +int camel_mbox_summary_save(CamelMboxSummary *s) +{ + int i, fd; + FILE *fp; + + printf("saving summary? %s\n", s->summary_path); + + /* FIXME: error checking */ + if (s->dirty) { + printf("yes\n"); + fd = open(s->summary_path, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fd == -1) + return -1; + fp = fdopen(fd, "w"); + if (fp == NULL) + return -1; + + s->dirty = FALSE; + + summary_header_write(fp, s); + encode_fixed_int(fp, s->messages->len); + + printf("message count = %d\n", s->messages->len); + + for (i=0;imessages->len;i++) { + message_struct_save(fp, g_ptr_array_index(s->messages, i)); + } + fclose(fp); + } else { + printf("no\n"); + } + return 0; +} + +CamelMboxMessageInfo *camel_mbox_summary_uid(CamelMboxSummary *s, const char *uid) +{ + return g_hash_table_lookup(s->message_uid, uid); +} + +CamelMboxMessageInfo *camel_mbox_summary_index(CamelMboxSummary *s, int index) +{ + return g_ptr_array_index(s->messages, index); +} + +int camel_mbox_summary_message_count(CamelMboxSummary *s) +{ + return s->messages->len; +} + +guint32 camel_mbox_summary_next_uid(CamelMboxSummary *s) +{ + guint32 uid = s->nextuid++; + + summary_header_save(s); + return uid; +} + +guint32 camel_mbox_summary_set_uid(CamelMboxSummary *s, guint32 uid) +{ + if (s->nextuid < uid) { + s->nextuid = uid; + summary_header_save(s); + } + return s->nextuid; +} + +#if 0 +int main(int argc, char **argv) +{ + if (argc<2) { + printf("usage: %s mbox\n", argv[0]); + return 1; + } + + gtk_init(&argc, &argv); + + index_folder(argv[1]); + + return 0; } +#endif diff --git a/camel/providers/mbox/camel-mbox-summary.h b/camel/providers/mbox/camel-mbox-summary.h index f8570f15c0..80b59ef54f 100644 --- a/camel/providers/mbox/camel-mbox-summary.h +++ b/camel/providers/mbox/camel-mbox-summary.h @@ -1,85 +1,78 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* - * - * Author : Bertrand Guiheneuf +/* + * Copyright (C) 2000 Helix Code Inc. * - * Copyright (C) 1999 Helix Code (http://www.helixcode.com). + * Authors: Michael Zucchi * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef MBOX_SUMMARY_H -#define MBOX_SUMMARY_H 1 - -#include - -#define CAMEL_MBOX_SUMMARY_TYPE (camel_mbox_summary_get_type ()) -#define CAMEL_MBOX_SUMMARY(obj) (GTK_CHECK_CAST((obj), CAMEL_MBOX_SUMMARY_TYPE, CamelMboxSummary)) -#define CAMEL_MBOX_SUMMARY_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_MBOX_SUMMARY_TYPE, CamelMboxSummaryClass)) -#define CAMEL_IS_MBOX_SUMMARY(o) (GTK_CHECK_TYPE((o), CAMEL_MBOX_SUMMARY_TYPE)) - - -#define CAMEL_MBOX_SUMMARY_VERSION 1 +#ifndef _CAMEL_MBOX_SUMMARY_H +#define _CAMEL_MBOX_SUMMARY_H +#include +#include +#include typedef struct { - CamelMessageInfo headers; + CamelMessageContentInfo info; - guint32 position; - guint size; - guint x_evolution_offset; - guint32 uid; - guchar status; + /* position in stream of this part */ + off_t pos; + off_t bodypos; + off_t endpos; +} CamelMboxMessageContentInfo; -} CamelMboxSummaryInformation; - - -/* this contains informations about the whole mbox file */ typedef struct { - CamelFolderSummary parent_object; + CamelMessageInfo info; - guint nb_message; /* number of messages in the summary */ - guint32 next_uid; - guint32 mbox_file_size; - guint32 mbox_modtime; + /* position of the xev header, if one exists */ + off_t xev_offset; +} CamelMboxMessageInfo; - GArray *message_info; /* array of CamelMboxSummaryInformation */ +typedef struct { + int dirty; /* if anything has changed */ -} CamelMboxSummary; + char *folder_path; + char *summary_path; + ibex *index; -typedef struct { - CamelFolderSummaryClass parent_class; + GPtrArray *messages; /* array of messages matching mbox order */ + GHashTable *message_uid; /* index to messages by uid */ -} CamelMboxSummaryClass; + int nextuid; + time_t time; /* time/size of folder's last update */ + size_t size; +} CamelMboxSummary; -GtkType camel_mbox_summary_get_type (void); +CamelMboxSummary *camel_mbox_summary_new(const char *summary, const char *folder, ibex *index); +void camel_mbox_summary_unref(CamelMboxSummary *); -void camel_mbox_summary_save (CamelMboxSummary *summary, - const gchar *filename, CamelException *ex); -CamelMboxSummary *camel_mbox_summary_load (const gchar *filename, - CamelException *ex); +int camel_mbox_summary_load(CamelMboxSummary *); +int camel_mbox_summary_save(CamelMboxSummary *); +int camel_mbox_summary_check(CamelMboxSummary *); -gboolean camel_mbox_summary_check_sync (gchar *summary_filename, - gchar *mbox_filename, - CamelException *ex); +guint32 camel_mbox_summary_next_uid(CamelMboxSummary *); +/* set the minimum uid */ +guint32 camel_mbox_summary_set_uid(CamelMboxSummary *s, guint32 uid); -void camel_mbox_summary_append_entries (CamelMboxSummary *summary, - GArray *entries); +CamelMboxMessageInfo *camel_mbox_summary_uid(CamelMboxSummary *s, const char *uid); +CamelMboxMessageInfo *camel_mbox_summary_index(CamelMboxSummary *, int index); +int camel_mbox_summary_message_count(CamelMboxSummary *); +/* TODO: should be in a utility library */ +int camel_mbox_summary_copy_block(int fromfd, int tofd, off_t readpos, size_t bytes); -#endif /* MBOX_SUMMARY_H */ +#endif /* ! _CAMEL_MBOX_SUMMARY_H */ diff --git a/camel/providers/mbox/camel-mbox-utils.c b/camel/providers/mbox/camel-mbox-utils.c index cd7da089e9..31710468b6 100644 --- a/camel/providers/mbox/camel-mbox-utils.c +++ b/camel/providers/mbox/camel-mbox-utils.c @@ -520,55 +520,3 @@ camel_mbox_write_xev (CamelMboxFolder *folder, g_free (tmp_file_name_secure); return next_free_uid; } - - - - - - -GArray * -parsed_information_to_mbox_summary (GArray *parsed_information) -{ - guint cur_msg; - CamelMboxParserMessageInfo *cur_msg_info; - GArray *mbox_summary; - CamelMboxSummaryInformation *cur_sum_info; - - mbox_summary = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation)); - mbox_summary = g_array_set_size (mbox_summary, parsed_information->len); - - for (cur_msg = 0; cur_msg < parsed_information->len; cur_msg++) { - - cur_msg_info = (CamelMboxParserMessageInfo *)(parsed_information->data) + cur_msg; - cur_sum_info = (CamelMboxSummaryInformation *)(mbox_summary->data) + cur_msg; - - cur_sum_info->position = cur_msg_info->message_position; - - cur_sum_info->size = cur_msg_info->size; - - cur_sum_info->x_evolution_offset = cur_msg_info->x_evolution_offset; - - cur_sum_info->uid = cur_msg_info->uid; - cur_sum_info->headers.uid = g_strdup_printf ("%d", - cur_sum_info->uid); - - cur_sum_info->status = cur_msg_info->status; - - cur_sum_info->headers.subject = cur_msg_info->subject; - cur_msg_info->subject = NULL; - - cur_sum_info->headers.sender = cur_msg_info->from; - cur_msg_info->from = NULL; - - cur_sum_info->headers.to = cur_msg_info->to; - cur_msg_info->to = NULL; - - /* XXX I'm guessing one of these is wrong. */ - cur_sum_info->headers.received_date = cur_msg_info->date; - cur_sum_info->headers.sent_date = g_strdup (cur_msg_info->date); - cur_msg_info->date = NULL; - - } - - return mbox_summary; -} -- cgit v1.2.3