diff options
Diffstat (limited to 'camel/camel-disco-diary.c')
-rw-r--r-- | camel/camel-disco-diary.c | 417 |
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); +} |