New source files implementing a basic journal.
authorJeffrey Stedfast <fejj@novell.com>
Tue, 14 Dec 2004 20:16:26 +0000 (20:16 +0000)
committerJeffrey Stedfast <fejj@src.gnome.org>
Tue, 14 Dec 2004 20:16:26 +0000 (20:16 +0000)
2004-12-14  Jeffrey Stedfast  <fejj@novell.com>

* providers/imap4/camel-imap4-journal.[c,h]: New source files
implementing a basic journal.

* providers/imap4/camel-imap4-summary.c
(camel_imap4_summary_flush_updates): Replay the journal.

* providers/imap4/camel-imap4-folder.c
(camel_imap4_folder_finalize): Write/unref the journal.
(camel_imap4_folder_new): Create a journal object.
(imap4_append_message): Journal the message append if we're in
offline mode.
(imap4_transfer_messages_to): Implement offline copy/move support
using the new journal code.

camel/ChangeLog
camel/providers/imap4/Makefile.am
camel/providers/imap4/camel-imap4-folder.c
camel/providers/imap4/camel-imap4-folder.h
camel/providers/imap4/camel-imap4-journal.c [new file with mode: 0644]
camel/providers/imap4/camel-imap4-journal.h [new file with mode: 0644]
camel/providers/imap4/camel-imap4-summary.c

index 2fc0e6a..e2d6ef4 100644 (file)
@@ -1,3 +1,19 @@
+2004-12-14  Jeffrey Stedfast  <fejj@novell.com>
+
+       * providers/imap4/camel-imap4-journal.[c,h]: New source files
+       implementing a basic journal.
+
+       * providers/imap4/camel-imap4-summary.c
+       (camel_imap4_summary_flush_updates): Replay the journal.
+
+       * providers/imap4/camel-imap4-folder.c
+       (camel_imap4_folder_finalize): Write/unref the journal.
+       (camel_imap4_folder_new): Create a journal object.
+       (imap4_append_message): Journal the message append if we're in
+       offline mode.
+       (imap4_transfer_messages_to): Implement offline copy/move support
+       using the new journal code.
+
 2004-12-09  Jeffrey Stedfast  <fejj@novell.com>
 
        * providers/imap4/camel-imap4-folder.c (imap4_append_message):
index 3cdf897..3cb8b85 100644 (file)
@@ -21,6 +21,8 @@ libcamelimap4_la_SOURCES =                    \
        camel-imap4-engine.h                    \
        camel-imap4-folder.c                    \
        camel-imap4-folder.h                    \
+       camel-imap4-journal.c                   \
+       camel-imap4-journal.h                   \
        camel-imap4-provider.c                  \
        camel-imap4-search.c                    \
        camel-imap4-search.h                    \
index 48524ac..0864c66 100644 (file)
@@ -48,6 +48,7 @@
 #include "camel-imap4-store.h"
 #include "camel-imap4-engine.h"
 #include "camel-imap4-folder.h"
+#include "camel-imap4-journal.h"
 #include "camel-imap4-stream.h"
 #include "camel-imap4-command.h"
 #include "camel-imap4-summary.h"
@@ -139,6 +140,7 @@ camel_imap4_folder_init (CamelIMAP4Folder *folder, CamelIMAP4FolderClass *klass)
        folder->sync_offline = FALSE;
        folder->utf7_name = NULL;
        folder->cachedir = NULL;
+       folder->journal = NULL;
        folder->search = NULL;
 }
 
@@ -152,6 +154,11 @@ camel_imap4_folder_finalize (CamelObject *object)
        if (folder->cache)
                camel_object_unref (folder->cache);
        
+       if (folder->journal) {
+               camel_imap4_journal_write (folder->journal, NULL);
+               camel_object_unref (folder->journal);
+       }
+       
        g_free (folder->utf7_name);
        g_free (folder->cachedir);
 }
@@ -163,7 +170,7 @@ imap4_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
        int i, count = 0;
        guint32 tag;
        
-       for (i = 0; i <args->argc; i++) {
+       for (i = 0; i < args->argc; i++) {
                CamelArgGet *arg = &args->argv[i];
                
                tag = arg->tag;
@@ -228,14 +235,21 @@ imap4_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
 
 
 static char *
-imap_get_summary_filename (const char *path)
+imap4_get_summary_filename (const char *path)
 {
        /* /path/to/imap/summary */
        return g_build_filename (path, "summary", NULL);
 }
 
 static char *
-imap_build_filename (const char *toplevel_dir, const char *full_name)
+imap4_get_journal_filename (const char *path)
+{
+       /* /path/to/imap/journal */
+       return g_build_filename (path, "journal", NULL);
+}
+
+static char *
+imap4_build_filename (const char *toplevel_dir, const char *full_name)
 {
        const char *inptr = full_name;
        int subdirs = 0;
@@ -277,14 +291,14 @@ imap_build_filename (const char *toplevel_dir, const char *full_name)
 }
 
 static char *
-imap_store_build_filename (void *store, const char *full_name)
+imap4_store_build_filename (void *store, const char *full_name)
 {
-       CamelIMAP4Store *imap_store = (CamelIMAP4Store *) store;
+       CamelIMAP4Store *imap4_store = (CamelIMAP4Store *) store;
        char *toplevel_dir;
        char *path;
        
-       toplevel_dir = g_strdup_printf ("%s/folders", imap_store->storage_path);
-       path = imap_build_filename (toplevel_dir, full_name);
+       toplevel_dir = g_strdup_printf ("%s/folders", imap4_store->storage_path);
+       path = imap4_build_filename (toplevel_dir, full_name);
        g_free (toplevel_dir);
        
        return path;
@@ -294,7 +308,7 @@ imap_store_build_filename (void *store, const char *full_name)
 CamelFolder *
 camel_imap4_folder_new (CamelStore *store, const char *full_name, CamelException *ex)
 {
-       CamelIMAP4Folder *imap_folder;
+       CamelIMAP4Folder *imap4_folder;
        char *utf7_name, *name, *p;
        CamelFolder *folder;
        char *path;
@@ -323,21 +337,25 @@ camel_imap4_folder_new (CamelStore *store, const char *full_name, CamelException
        
        utf7_name = camel_utf8_utf7 (utf7_name);
        
-       folder = (CamelFolder *) (imap_folder = (CamelIMAP4Folder *) camel_object_new (CAMEL_TYPE_IMAP4_FOLDER));
+       folder = (CamelFolder *) (imap4_folder = (CamelIMAP4Folder *) camel_object_new (CAMEL_TYPE_IMAP4_FOLDER));
        camel_folder_construct (folder, store, full_name, name);
-       imap_folder->utf7_name = utf7_name;
+       imap4_folder->utf7_name = utf7_name;
        
        folder->summary = camel_imap4_summary_new (folder);
-       imap_folder->cachedir = imap_store_build_filename (store, folder->full_name);
-       camel_mkdir (imap_folder->cachedir, 0777);
+       imap4_folder->cachedir = imap4_store_build_filename (store, folder->full_name);
+       camel_mkdir (imap4_folder->cachedir, 0777);
        
-       imap_folder->cache = camel_data_cache_new (imap_folder->cachedir, 0, NULL);
+       imap4_folder->cache = camel_data_cache_new (imap4_folder->cachedir, 0, NULL);
        
-       path = imap_get_summary_filename (imap_folder->cachedir);
+       path = imap4_get_summary_filename (imap4_folder->cachedir);
        camel_folder_summary_set_filename (folder->summary, path);
        g_free (path);
        
-       imap_folder->search = camel_imap4_search_new (((CamelIMAP4Store *) store)->engine, imap_folder->cachedir);
+       path = imap4_get_journal_filename (imap4_folder->cachedir);
+       imap4_folder->journal = camel_imap4_journal_new (imap4_folder, path);
+       g_free (path);
+       
+       imap4_folder->search = camel_imap4_search_new (((CamelIMAP4Store *) store)->engine, imap4_folder->cachedir);
        
        if (camel_session_is_online (((CamelService *) store)->session)) {
                /* we don't care if the summary loading fails here */
@@ -707,12 +725,12 @@ untagged_fetch (CamelIMAP4Engine *engine, CamelIMAP4Command *ic, guint32 index,
                        }
                } else {
                        /* wtf? */
-                       fprintf (stderr, "huh? %s?...\n", token->v.atom);
+                       d(fprintf (stderr, "huh? %s?...\n", token->v.atom));
                }
        } while (1);
        
        if (token->token != ')') {
-               fprintf (stderr, "expected ')' to close untagged FETCH response\n");
+               d(fprintf (stderr, "expected ')' to close untagged FETCH response\n"));
                goto unexpected;
        }
        
@@ -732,7 +750,7 @@ imap4_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
 {
        CamelIMAP4Engine *engine = ((CamelIMAP4Store *) folder->parent_store)->engine;
        CamelSession *session = ((CamelService *) folder->parent_store)->session;
-       CamelIMAP4Folder *imap_folder = (CamelIMAP4Folder *) folder;
+       CamelIMAP4Folder *imap4_folder = (CamelIMAP4Folder *) folder;
        CamelMimeMessage *message = NULL;
        CamelStream *stream, *cache;
        CamelIMAP4Command *ic;
@@ -740,7 +758,7 @@ imap4_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
        
        CAMEL_SERVICE_LOCK (folder->parent_store, connect_lock);
        
-       if (imap_folder->cache && (stream = camel_data_cache_get (imap_folder->cache, "cache", uid, ex))) {
+       if (imap4_folder->cache && (stream = camel_data_cache_get (imap4_folder->cache, "cache", uid, ex))) {
                message = camel_mime_message_new ();
                
                if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream) == -1) {
@@ -812,10 +830,10 @@ imap4_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
                camel_stream_reset (stream);
                
                /* cache the message locally */
-               if (imap_folder->cache && (cache = camel_data_cache_add (imap_folder->cache, "cache", uid, NULL))) {
+               if (imap4_folder->cache && (cache = camel_data_cache_add (imap4_folder->cache, "cache", uid, NULL))) {
                        if (camel_stream_write_to_stream (stream, cache) == -1
                            || camel_stream_flush (cache) == -1)
-                               camel_data_cache_remove (imap_folder->cache, "cache", uid, NULL);
+                               camel_data_cache_remove (imap4_folder->cache, "cache", uid, NULL);
                        camel_object_unref (cache);
                }
                
@@ -870,7 +888,7 @@ imap4_append_message (CamelFolder *folder, CamelMimeMessage *message,
                *appended_uid = NULL;
        
        if (!camel_session_is_online (session)) {
-               camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot append messages to IMAP folders in offline mode."));
+               camel_imap4_journal_append (((CamelIMAP4Folder *) folder)->journal, message, info, ex);
                return;
        }
        
@@ -1029,16 +1047,8 @@ imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest
        GPtrArray *infos;
        char *set;
        
-       if (!camel_session_is_online (session)) {
-               if (move)
-                       camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
-                                            _("Cannot move messages to or from IMAP folders in offline mode."));
-               else
-                       camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
-                                            _("Cannot copy messages to or from IMAP folders in offline mode."));
-               
-               return;
-       }
+       if (transferred_uids)
+               *transferred_uids = NULL;
        
        infos = g_ptr_array_new ();
        for (i = 0; i < uids->len; i++) {
@@ -1057,6 +1067,30 @@ imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest
        
        CAMEL_SERVICE_LOCK (src->parent_store, connect_lock);
        
+       /* check for offline operation */
+       if (!camel_session_is_online (session)) {
+               CamelMimeMessage *message;
+               
+               for (i = 0; i < infos->len; i++) {
+                       info = infos->pdata[i];
+                       
+                       if (!(message = imap4_get_message (src, camel_message_info_uid (info), ex)))
+                               break;
+                       
+                       camel_imap4_journal_append (((CamelIMAP4Folder *) dest)->journal, message, info, ex);
+                       camel_object_unref (message);
+                       
+                       if (camel_exception_is_set (ex))
+                               break;
+                       
+                       if (move)
+                               camel_folder_set_message_flags (src, camel_message_info_uid (info),
+                                                               CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
+               }
+               
+               goto done;
+       }
+       
        dest_namelen = strlen (camel_imap4_folder_utf7_name ((CamelIMAP4Folder *) dest));
        
        for (i = 0; i < infos->len; i += n) {
@@ -1119,7 +1153,7 @@ imap4_transfer_messages_to (CamelFolder *src, GPtrArray *uids, CamelFolder *dest
  done:
        
        for (i = 0; i < infos->len; i++)
-               camel_message_info_free(infos->pdata[i]);
+               camel_message_info_free (infos->pdata[i]);
        g_ptr_array_free (infos, TRUE);
        
        CAMEL_SERVICE_LOCK (src->parent_store, connect_lock);
index ae0263f..4a4b487 100644 (file)
@@ -40,6 +40,8 @@ extern "C" {
 typedef struct _CamelIMAP4Folder CamelIMAP4Folder;
 typedef struct _CamelIMAP4FolderClass CamelIMAP4FolderClass;
 
+struct _CamelIMAP4Journal;
+
 enum {
        CAMEL_IMAP4_FOLDER_ARG_SYNC_OFFLINE = CAMEL_FOLDER_ARG_LAST,
        CAMEL_IMAP4_FOLDER_ARG_LAST = CAMEL_FOLDER_ARG_LAST + 0x100
@@ -56,6 +58,8 @@ struct _CamelIMAP4Folder {
        unsigned int sync_offline:1;
        
        CamelFolderSearch *search;
+       
+       struct _CamelIMAP4Journal *journal;
        CamelDataCache *cache;
        
        char *cachedir;
diff --git a/camel/providers/imap4/camel-imap4-journal.c b/camel/providers/imap4/camel-imap4-journal.c
new file mode 100644 (file)
index 0000000..77e9727
--- /dev/null
@@ -0,0 +1,383 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@novell.com>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <camel/camel-i18n.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-file-utils.h>
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-data-cache.h>
+
+#include "camel-imap4-folder.h"
+#include "camel-imap4-journal.h"
+
+
+#define d(x) x
+
+
+static void camel_imap4_journal_class_init (CamelIMAP4JournalClass *klass);
+static void camel_imap4_journal_init (CamelIMAP4Journal *journal, CamelIMAP4JournalClass *klass);
+static void camel_imap4_journal_finalize (CamelObject *object);
+
+
+static CamelObjectClass *parent_class = NULL;
+
+
+CamelType
+camel_imap4_journal_get_type (void)
+{
+       static CamelType type = 0;
+       
+       if (!type) {
+               type = camel_type_register (camel_object_get_type (),
+                                           "CamelIMAP4Journal",
+                                           sizeof (CamelIMAP4Journal),
+                                           sizeof (CamelIMAP4JournalClass),
+                                           (CamelObjectClassInitFunc) camel_imap4_journal_class_init,
+                                           NULL,
+                                           (CamelObjectInitFunc) camel_imap4_journal_init,
+                                           (CamelObjectFinalizeFunc) camel_imap4_journal_finalize);
+       }
+       
+       return type;
+}
+
+static void
+camel_imap4_journal_class_init (CamelIMAP4JournalClass *klass)
+{
+       parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE);
+}
+
+static void
+camel_imap4_journal_init (CamelIMAP4Journal *journal, CamelIMAP4JournalClass *klass)
+{
+       journal->folder = NULL;
+       journal->filename = NULL;
+       e_dlist_init (&journal->queue);
+}
+
+static void
+camel_imap4_journal_finalize (CamelObject *object)
+{
+       CamelIMAP4Journal *journal = (CamelIMAP4Journal *) object;
+       CamelIMAP4JournalEntry *entry;
+       
+       g_free (journal->filename);
+       
+       while ((entry = (CamelIMAP4JournalEntry *) e_dlist_remhead (&journal->queue))) {
+               g_free (entry->v.append_uid);
+               g_free (entry);
+       }
+}
+
+
+static CamelIMAP4JournalEntry *
+imap4_journal_entry_load (FILE *in)
+{
+       CamelIMAP4JournalEntry *entry;
+       
+       entry = g_malloc0 (sizeof (CamelIMAP4JournalEntry));
+       
+       if (camel_file_util_decode_uint32 (in, &entry->type) == -1)
+               goto exception;
+       
+       switch (entry->type) {
+       case CAMEL_IMAP4_JOURNAL_ENTRY_APPEND:
+               if (camel_file_util_decode_string (in, &entry->v.append_uid) == -1)
+                       goto exception;
+               
+               break;
+       default:
+               goto exception;
+       }
+       
+       return entry;
+       
+ exception:
+       
+       switch (entry->type) {
+       case CAMEL_IMAP4_JOURNAL_ENTRY_APPEND:
+               g_free (entry->v.append_uid);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       
+       g_free (entry);
+       
+       return NULL;
+}
+
+
+CamelIMAP4Journal *
+camel_imap4_journal_new (CamelIMAP4Folder *folder, const char *filename)
+{
+       CamelIMAP4Journal *journal;
+       EDListNode *entry;
+       FILE *fp;
+       
+       g_return_val_if_fail (CAMEL_IS_IMAP4_FOLDER (folder), NULL);
+       
+       journal = (CamelIMAP4Journal *) camel_object_new (camel_imap4_journal_get_type ());
+       journal->filename = g_strdup (filename);
+       journal->folder = folder;
+       
+       if ((fp = fopen (filename, "r"))) {
+               while ((entry = (EDListNode *) imap4_journal_entry_load (fp)))
+                       e_dlist_addtail (&journal->queue, entry);
+               
+               fclose (fp);
+       }
+       
+       return journal;
+}
+
+
+void
+camel_imap4_journal_set_filename (CamelIMAP4Journal *journal, const char *filename)
+{
+       g_return_if_fail (CAMEL_IS_IMAP4_JOURNAL (journal));
+       
+       g_free (journal->filename);
+       journal->filename = g_strdup (filename);
+}
+
+
+static int
+imap4_journal_entry_write (CamelIMAP4JournalEntry *entry, FILE *out)
+{
+       if (camel_file_util_encode_uint32 (out, entry->type) == -1)
+               return -1;
+       
+       switch (entry->type) {
+       case CAMEL_IMAP4_JOURNAL_ENTRY_APPEND:
+               if (camel_file_util_encode_string (out, entry->v.append_uid))
+                       return -1;
+               
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       
+       return 0;
+}
+
+
+int
+camel_imap4_journal_write (CamelIMAP4Journal *journal, CamelException *ex)
+{
+       CamelIMAP4JournalEntry *entry;
+       EDListNode *node;
+       FILE *fp;
+       int fd;
+       
+       if ((fd = open (journal->filename, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1) {
+               camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+                                     _("Cannot write IMAP4 offline journal: %s"),
+                                     g_strerror (errno));
+               return -1;
+       }
+       
+       fp = fdopen (fd, "w");
+       node = journal->queue.head;
+       while (node->next) {
+               entry = (CamelIMAP4JournalEntry *) node;
+               if (imap4_journal_entry_write (entry, fp) == -1)
+                       goto exception;
+               node = node->next;
+       }
+       
+       if (fsync (fd) == -1)
+               goto exception;
+       
+       fclose (fp);
+       
+       return 0;
+       
+ exception:
+       
+       camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+                             _("Cannot write IMAP4 offline journal: %s"),
+                             g_strerror (errno));
+       
+       fclose (fp);
+       
+       return -1;
+}
+
+
+static int
+imap4_journal_entry_play_append (CamelIMAP4Journal *journal, CamelIMAP4JournalEntry *entry, CamelException *ex)
+{
+       CamelIMAP4Folder *imap4_folder = journal->folder;
+       CamelFolder *folder = (CamelFolder *) imap4_folder;
+       CamelMimeMessage *message;
+       CamelMessageInfo *info;
+       CamelStream *stream;
+       CamelException lex;
+       
+       /* if the message isn't in the cache, the user went behind our backs so "not our problem" */
+       if (!imap4_folder->cache || !(stream = camel_data_cache_get (imap4_folder->cache, "cache", entry->v.append_uid, ex)))
+               goto done;
+       
+       message = camel_mime_message_new ();
+       if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream) == -1) {
+               camel_object_unref (message);
+               camel_object_unref (stream);
+               goto done;
+       }
+       
+       camel_object_unref (stream);
+       
+       if (!(info = camel_folder_summary_uid (folder->summary, entry->v.append_uid))) {
+               /* info not in the summary, either because the summary
+                * got corrupted or because the previous time this
+                * journal was replay'd, it failed [1] */
+               info = camel_message_info_new (NULL);
+       }
+       
+       camel_exception_init (&lex);
+       camel_folder_append_message (folder, message, info, NULL, &lex);
+       camel_message_info_free (info);
+       camel_object_unref (message);
+       
+       if (camel_exception_is_set (&lex)) {
+               /* [1] remove the summary even if we fail or the next
+                * summary downsync will break because info indexes
+                * will be wrong
+                *
+                * FIXME: we really need to save these info's to a
+                * temp location and then restore them after the
+                * summary downsync finishes. */
+               camel_folder_summary_remove_uid (folder->summary, entry->v.append_uid);
+               camel_exception_xfer (ex, &lex);
+               return -1;
+       }
+       
+ done:
+       
+       camel_folder_summary_remove_uid (folder->summary, entry->v.append_uid);
+       camel_data_cache_remove (journal->folder->cache, "cache", entry->v.append_uid, NULL);
+       
+       return 0;
+}
+
+static int
+imap4_journal_entry_play (CamelIMAP4Journal *journal, CamelIMAP4JournalEntry *entry, CamelException *ex)
+{
+       switch (entry->type) {
+       case CAMEL_IMAP4_JOURNAL_ENTRY_APPEND:
+               return imap4_journal_entry_play_append (journal, entry, ex);
+       default:
+               g_assert_not_reached ();
+               return -1;
+       }
+}
+
+
+int
+camel_imap4_journal_replay (CamelIMAP4Journal *journal, CamelException *ex)
+{
+       EDListNode *node, *next;
+       CamelException lex;
+       int failed = 0;
+       
+       camel_exception_init (&lex);
+       
+       node = journal->queue.head;
+       while (node->next) {
+               next = node->next;
+               if (imap4_journal_entry_play (journal, (CamelIMAP4JournalEntry *) node, &lex) == -1) {
+                       if (failed == 0)
+                               camel_exception_xfer (ex, &lex);
+                       camel_exception_clear (&lex);
+                       failed++;
+               } else {
+                       e_dlist_remove (node);
+               }
+               node = next;
+       }
+       
+       if (failed > 0)
+               return -1;
+       
+       return 0;
+}
+
+
+void
+camel_imap4_journal_append (CamelIMAP4Journal *journal, CamelMimeMessage *message, CamelMessageInfo *mi, CamelException *ex)
+{
+       CamelFolder *folder = (CamelFolder *) journal->folder;
+       CamelIMAP4JournalEntry *entry;
+       CamelStream *cache;
+       guint32 nextuid;
+       char *uid;
+       
+       if (journal->folder->cache == NULL) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
+                                    _("Cannot append message in offline mode: cache unavailable"));
+               return;
+       }
+       
+       nextuid = camel_folder_summary_next_uid (folder->summary);
+       uid = g_strdup_printf ("-%u", nextuid);
+       
+       if (!(cache = camel_data_cache_add (journal->folder->cache, "cache", uid, ex))) {
+               folder->summary->nextuid--;
+               g_free (uid);
+               return;
+       }
+       
+       if (camel_data_wrapper_write_to_stream ((CamelDataWrapper *) message, cache) == -1
+           || camel_stream_flush (cache) == -1) {
+               camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+                                     _("Cannot append message in offline mode: %s"),
+                                     g_strerror (errno));
+               camel_data_cache_remove (journal->folder->cache, "cache", uid, NULL);
+               folder->summary->nextuid--;
+               camel_object_unref (cache);
+               g_free (uid);
+               return;
+       }
+       
+       camel_object_unref (cache);
+       
+       entry = g_new (CamelIMAP4JournalEntry, 1);
+       entry->type = CAMEL_IMAP4_JOURNAL_ENTRY_APPEND;
+       entry->v.append_uid = uid;
+       
+       e_dlist_addtail (&journal->queue, (EDListNode *) entry);
+}
diff --git a/camel/providers/imap4/camel-imap4-journal.h b/camel/providers/imap4/camel-imap4-journal.h
new file mode 100644 (file)
index 0000000..2356bd4
--- /dev/null
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@novell.com>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CAMEL_IMAP4_JOURNAL_H__
+#define __CAMEL_IMAP4_JOURNAL_H__
+
+#include <stdarg.h>
+
+#include <glib.h>
+
+#include <libedataserver/e-msgport.h>
+#include <camel/camel-mime-message.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define CAMEL_TYPE_IMAP4_JOURNAL            (camel_imap4_journal_get_type ())
+#define CAMEL_IMAP4_JOURNAL(obj)            (CAMEL_CHECK_CAST ((obj), CAMEL_TYPE_IMAP4_JOURNAL, CamelIMAP4Journal))
+#define CAMEL_IMAP4_JOURNAL_CLASS(klass)    (CAMEL_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_IMAP4_JOURNAL, CamelIMAP4JournalClass))
+#define CAMEL_IS_IMAP4_JOURNAL(obj)         (CAMEL_CHECK_TYPE ((obj), CAMEL_TYPE_IMAP4_JOURNAL))
+#define CAMEL_IS_IMAP4_JOURNAL_CLASS(klass) (CAMEL_CHECK_CLASS_TYPE ((klass), CAMEL_TYPE_IMAP4_JOURNAL))
+#define CAMEL_IMAP4_JOURNAL_GET_CLASS(obj)  (CAMEL_CHECK_GET_CLASS ((obj), CAMEL_TYPE_IMAP4_JOURNAL, CamelIMAP4JournalClass))
+
+typedef struct _CamelIMAP4Journal CamelIMAP4Journal;
+typedef struct _CamelIMAP4JournalClass CamelIMAP4JournalClass;
+typedef struct _CamelIMAP4JournalEntry CamelIMAP4JournalEntry;
+
+struct _CamelIMAP4Folder;
+
+enum {
+       CAMEL_IMAP4_JOURNAL_ENTRY_APPEND,
+};
+
+struct _CamelIMAP4JournalEntry {
+       EDListNode node;
+       
+       int type;
+       
+       union {
+               char *append_uid;
+       } v;
+};
+
+struct _CamelIMAP4Journal {
+       CamelObject parent_object;
+       
+       struct _CamelIMAP4Folder *folder;
+       char *filename;
+       EDList queue;
+};
+
+struct _CamelIMAP4JournalClass {
+       CamelObjectClass parent_class;
+       
+};
+
+
+CamelType camel_imap4_journal_get_type (void);
+
+CamelIMAP4Journal *camel_imap4_journal_new (struct _CamelIMAP4Folder *folder, const char *filename);
+
+void camel_imap4_journal_set_filename (CamelIMAP4Journal *journal, const char *filename);
+
+int camel_imap4_journal_write (CamelIMAP4Journal *journal, CamelException *ex);
+
+int camel_imap4_journal_replay (CamelIMAP4Journal *journal, CamelException *ex);
+
+/* interfaces for adding a journal entry */
+void camel_imap4_journal_append (CamelIMAP4Journal *journal, CamelMimeMessage *message, CamelMessageInfo *mi, CamelException *ex);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CAMEL_IMAP4_JOURNAL_H__ */
index 598f16f..99d3144 100644 (file)
@@ -44,6 +44,7 @@
 #include "camel-imap4-folder.h"
 #include "camel-imap4-stream.h"
 #include "camel-imap4-command.h"
+#include "camel-imap4-journal.h"
 #include "camel-imap4-utils.h"
 
 #include "camel-imap4-summary.h"
@@ -1252,6 +1253,9 @@ camel_imap4_summary_flush_updates (CamelFolderSummary *summary, CamelException *
        
        g_return_val_if_fail (CAMEL_IS_IMAP4_SUMMARY (summary), -1);
        
+       /* FIXME: what do we do if replaying the journal fails? */
+       camel_imap4_journal_replay (((CamelIMAP4Folder *) summary->folder)->journal, NULL);
+       
        engine = ((CamelIMAP4Store *) summary->folder->parent_store)->engine;
        scount = camel_folder_summary_count (summary);