aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-disco-diary.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-disco-diary.c')
-rw-r--r--camel/camel-disco-diary.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/camel/camel-disco-diary.c b/camel/camel-disco-diary.c
new file mode 100644
index 0000000000..5132168bab
--- /dev/null
+++ b/camel/camel-disco-diary.c
@@ -0,0 +1,417 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-disco-diary.c: class for a disconnected operation log */
+
+/*
+ * Authors: Dan Winship <danw@ximian.com>
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "camel-disco-diary.h"
+#include "camel-disco-folder.h"
+#include "camel-disco-store.h"
+#include "camel-exception.h"
+#include "camel-file-utils.h"
+#include "camel-folder.h"
+#include "camel-operation.h"
+#include "camel-session.h"
+#include "camel-store.h"
+
+#include <errno.h>
+
+
+static void
+camel_disco_diary_class_init (CamelDiscoDiaryClass *camel_disco_diary_class)
+{
+ /* virtual method definition */
+}
+
+static void
+camel_disco_diary_init (CamelDiscoDiary *diary)
+{
+ diary->folders = g_hash_table_new (g_str_hash, g_str_equal);
+ diary->uidmap = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+static void
+unref_folder (gpointer key, gpointer value, gpointer data)
+{
+ camel_object_unref (value);
+}
+
+static void
+free_uid (gpointer key, gpointer value, gpointer data)
+{
+ g_free (key);
+ g_free (value);
+}
+
+static void
+camel_disco_diary_finalize (CamelDiscoDiary *diary)
+{
+ if (diary->file)
+ fclose (diary->file);
+ if (diary->folders) {
+ g_hash_table_foreach (diary->folders, unref_folder, NULL);
+ g_hash_table_destroy (diary->folders);
+ }
+ if (diary->uidmap) {
+ g_hash_table_foreach (diary->folders, free_uid, NULL);
+ g_hash_table_destroy (diary->uidmap);
+ }
+}
+
+CamelType
+camel_disco_diary_get_type (void)
+{
+ static CamelType camel_disco_diary_type = CAMEL_INVALID_TYPE;
+
+ if (camel_disco_diary_type == CAMEL_INVALID_TYPE) {
+ camel_disco_diary_type = camel_type_register (
+ CAMEL_OBJECT_TYPE, "CamelDiscoDiary",
+ sizeof (CamelDiscoDiary),
+ sizeof (CamelDiscoDiaryClass),
+ (CamelObjectClassInitFunc) camel_disco_diary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_disco_diary_init,
+ (CamelObjectFinalizeFunc) camel_disco_diary_finalize);
+ }
+
+ return camel_disco_diary_type;
+}
+
+
+static int
+diary_encode_uids (CamelDiscoDiary *diary, GPtrArray *uids)
+{
+ int i, status;
+
+ status = camel_file_util_encode_uint32 (diary->file, uids->len);
+ for (i = 0; status != -1 && i < uids->len; i++)
+ status = camel_file_util_encode_string (diary->file, uids->pdata[i]);
+ return status;
+}
+
+void
+camel_disco_diary_log (CamelDiscoDiary *diary, CamelDiscoDiaryAction action,
+ ...)
+{
+ va_list ap;
+ int status;
+
+ /* You may already be a loser. */
+ if (!diary->file)
+ return;
+
+ status = camel_file_util_encode_uint32 (diary->file, action);
+ if (status == -1)
+ goto lose;
+
+ va_start (ap, action);
+ switch (action) {
+ case CAMEL_DISCO_DIARY_FOLDER_EXPUNGE:
+ {
+ CamelFolder *folder = va_arg (ap, CamelFolder *);
+ GPtrArray *uids = va_arg (ap, GPtrArray *);
+
+ status = camel_file_util_encode_string (diary->file, folder->full_name);
+ if (status != -1)
+ status = diary_encode_uids (diary, uids);
+ break;
+ }
+
+ case CAMEL_DISCO_DIARY_FOLDER_APPEND:
+ {
+ CamelFolder *folder = va_arg (ap, CamelFolder *);
+ char *uid = va_arg (ap, char *);
+
+ status = camel_file_util_encode_string (diary->file, folder->full_name);
+ if (status != -1)
+ status = camel_file_util_encode_string (diary->file, uid);
+ break;
+ }
+
+ case CAMEL_DISCO_DIARY_FOLDER_MOVE:
+ case CAMEL_DISCO_DIARY_FOLDER_COPY:
+ {
+ CamelFolder *source = va_arg (ap, CamelFolder *);
+ CamelFolder *destination = va_arg (ap, CamelFolder *);
+ GPtrArray *uids = va_arg (ap, GPtrArray *);
+
+ status = camel_file_util_encode_string (diary->file, source->full_name);
+ if (status == -1)
+ break;
+ status = camel_file_util_encode_string (diary->file, destination->full_name);
+ if (status == -1)
+ break;
+ status = diary_encode_uids (diary, uids);
+ break;
+ }
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ va_end (ap);
+
+ lose:
+ if (status == -1) {
+ char *msg;
+
+ msg = g_strdup_printf (_("Could not write log entry: %s\n"
+ "Further operations on this server "
+ "will not be replayed when you\n"
+ "reconnect to the network."),
+ g_strerror (errno));
+ camel_session_alert_user (camel_service_get_session (CAMEL_SERVICE (diary->store)),
+ CAMEL_SESSION_ALERT_ERROR,
+ msg, FALSE);
+ g_free (msg);
+
+ fclose (diary->file);
+ diary->file = NULL;
+ }
+}
+
+static void
+free_uids (GPtrArray *array)
+{
+ while (array->len--)
+ g_free (array->pdata[array->len]);
+ g_ptr_array_free (array, TRUE);
+}
+
+static GPtrArray *
+diary_decode_uids (CamelDiscoDiary *diary)
+{
+ GPtrArray *uids;
+ char *uid;
+ guint32 i;
+
+ if (camel_file_util_decode_uint32 (diary->file, &i) == -1)
+ return NULL;
+ uids = g_ptr_array_new ();
+ while (i--) {
+ if (camel_file_util_decode_string (diary->file, &uid) == -1) {
+ free_uids (uids);
+ return NULL;
+ }
+ g_ptr_array_add (uids, uid);
+ }
+
+ return uids;
+}
+
+static CamelFolder *
+diary_decode_folder (CamelDiscoDiary *diary)
+{
+ CamelFolder *folder;
+ char *name;
+
+ if (camel_file_util_decode_string (diary->file, &name) == -1)
+ return NULL;
+ folder = g_hash_table_lookup (diary->folders, name);
+ if (!folder) {
+ CamelException ex;
+ char *msg;
+
+ camel_exception_init (&ex);
+ folder = camel_store_get_folder (CAMEL_STORE (diary->store),
+ name, 0, &ex);
+ if (folder)
+ g_hash_table_insert (diary->folders, name, folder);
+ else {
+ msg = g_strdup_printf (_("Could not open `%s':\n%s\nChanges made to this folder will not be resynchronized."),
+ name, camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ camel_session_alert_user (camel_service_get_session (CAMEL_SERVICE (diary->store)),
+ CAMEL_SESSION_ALERT_WARNING,
+ msg, FALSE);
+ g_free (msg);
+ g_free (name);
+ }
+ } else
+ g_free (name);
+ return folder;
+}
+
+static void
+close_folder (gpointer name, gpointer folder, gpointer data)
+{
+ g_free (name);
+ camel_folder_sync (folder, FALSE, NULL);
+ camel_object_unref (folder);
+}
+
+void
+camel_disco_diary_replay (CamelDiscoDiary *diary, CamelException *ex)
+{
+ guint32 action;
+ off_t size;
+ double pc;
+
+ fseeko (diary->file, 0, SEEK_END);
+ size = ftello (diary->file);
+ g_return_if_fail (size != 0);
+ rewind (diary->file);
+
+ camel_operation_start (NULL, _("Resynchronizing with server"));
+ while (!camel_exception_is_set (ex)) {
+ pc = ftello (diary->file) / size;
+ camel_operation_progress (NULL, pc * 100);
+
+ if (camel_file_util_decode_uint32 (diary->file, &action) == -1)
+ break;
+ if (action == CAMEL_DISCO_DIARY_END)
+ break;
+
+ switch (action) {
+ case CAMEL_DISCO_DIARY_FOLDER_EXPUNGE:
+ {
+ CamelFolder *folder;
+ GPtrArray *uids;
+
+ folder = diary_decode_folder (diary);
+ uids = diary_decode_uids (diary);
+ if (!uids)
+ goto lose;
+
+ if (folder)
+ camel_disco_folder_expunge_uids (folder, uids, ex);
+ free_uids (uids);
+ break;
+ }
+
+ case CAMEL_DISCO_DIARY_FOLDER_APPEND:
+ {
+ CamelFolder *folder;
+ char *uid;
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+
+ folder = diary_decode_folder (diary);
+ if (camel_file_util_decode_string (diary->file, &uid) == -1)
+ goto lose;
+
+ if (!folder) {
+ g_free (uid);
+ continue;
+ }
+
+ message = camel_folder_get_message (folder, uid, NULL);
+ if (!message) {
+ /* The message was appended and then deleted. */
+ g_free (uid);
+ continue;
+ }
+ info = camel_folder_get_message_info (folder, uid);
+
+ camel_folder_append_message (folder, message, info, ex);
+ g_free (uid);
+ camel_folder_free_message_info (folder, info);
+
+ break;
+ }
+
+ case CAMEL_DISCO_DIARY_FOLDER_COPY:
+ case CAMEL_DISCO_DIARY_FOLDER_MOVE:
+ {
+ CamelFolder *source, *destination;
+ GPtrArray *uids;
+
+ source = diary_decode_folder (diary);
+ destination = diary_decode_folder (diary);
+ uids = diary_decode_uids (diary);
+ if (!uids)
+ goto lose;
+
+ if (!source || !destination) {
+ free_uids (uids);
+ continue;
+ }
+
+ if (action == CAMEL_DISCO_DIARY_FOLDER_COPY)
+ camel_folder_copy_messages_to (source, uids, destination, ex);
+ else
+ camel_folder_move_messages_to (source, uids, destination, ex);
+ free_uids (uids);
+ break;
+ }
+
+ }
+ }
+
+ lose:
+ camel_operation_end (NULL);
+
+ /* Close folders */
+ g_hash_table_foreach (diary->folders, close_folder, diary);
+ g_hash_table_destroy (diary->folders);
+ diary->folders = NULL;
+
+ /* Truncate the log */
+ ftruncate (fileno (diary->file), 0);
+}
+
+CamelDiscoDiary *
+camel_disco_diary_new (CamelDiscoStore *store, const char *filename, CamelException *ex)
+{
+ CamelDiscoDiary *diary;
+
+ g_return_val_if_fail (CAMEL_IS_DISCO_STORE (store), NULL);
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ diary = CAMEL_DISCO_DIARY (camel_object_new (CAMEL_DISCO_DIARY_TYPE));
+ diary->store = store;
+
+ diary->file = fopen (filename, "a+");
+ if (!diary->file) {
+ camel_object_unref (CAMEL_OBJECT (diary));
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ "Could not open journal file: %s",
+ g_strerror (errno));
+ return NULL;
+ }
+
+ return diary;
+}
+
+gboolean
+camel_disco_diary_empty (CamelDiscoDiary *diary)
+{
+ return ftello (diary->file) == 0;
+}
+
+void
+camel_disco_diary_uidmap_add (CamelDiscoDiary *diary, const char *old_uid,
+ const char *new_uid)
+{
+ g_hash_table_insert (diary->uidmap, g_strdup (old_uid),
+ g_strdup (new_uid));
+}
+
+const char *
+camel_disco_diary_uidmap_lookup (CamelDiscoDiary *diary, const char *uid)
+{
+ return g_hash_table_lookup (diary->uidmap, uid);
+}