From 11ea26d0e33405490795a766406d5e065b6b9234 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 29 May 2001 17:08:53 +0000 Subject: Code for logging and replaying offline operations. * camel-disco-diary.c: Code for logging and replaying offline operations. * camel-disco-store.c (disco_construct): Set disco->status here (where we can base it on the session's offline status) rather than at init time. (disco_connect): If we connect online and have a non-empty diary, switch to RESYNCING mode and replay the diary to the server. (disco_get_folder, disco_get_folder_info): Add _resyncing variants. * camel-disco-folder.c (disco_sync, disco_expunge_uids, disco_append_message, disco_copy_messages_to, disco_move_messages_to): Add _resyncing variants to switches. (disco_expunge_uids, disco_append_message, disco_copy_messages_to, disco_move_messages_to): Remove #ifdef'ed out diary code: let the provider do it. (disco_append_message): Redo the append methods to no longer return the UID, since we're no longer doing the logging from here. svn path=/trunk/; revision=10040 --- camel/ChangeLog | 66 +++++++ camel/Makefile.am | 2 + camel/camel-disco-diary.c | 417 +++++++++++++++++++++++++++++++++++++++++++++ camel/camel-disco-diary.h | 100 +++++++++++ camel/camel-disco-folder.c | 54 +++--- camel/camel-disco-folder.h | 47 +++-- camel/camel-disco-store.c | 66 +++++-- camel/camel-disco-store.h | 27 ++- camel/camel-types.h | 1 + camel/camel.h | 1 + 10 files changed, 706 insertions(+), 75 deletions(-) create mode 100644 camel/camel-disco-diary.c create mode 100644 camel/camel-disco-diary.h diff --git a/camel/ChangeLog b/camel/ChangeLog index e09c0532e1..ae54828283 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,69 @@ +2001-05-29 Dan Winship + + * camel-disco-diary.c: Code for logging and replaying offline + operations. + + * camel-disco-store.c (disco_construct): Set disco->status here + (where we can base it on the session's offline status) rather than + at init time. + (disco_connect): If we connect online and have a non-empty diary, + switch to RESYNCING mode and replay the diary to the server. + (disco_get_folder, disco_get_folder_info): Add _resyncing + variants. + + * camel-disco-folder.c (disco_sync, disco_expunge_uids, + disco_append_message, disco_copy_messages_to, + disco_move_messages_to): Add _resyncing variants to switches. + (disco_expunge_uids, disco_append_message, disco_copy_messages_to, + disco_move_messages_to): Remove #ifdef'ed out diary code: let the + provider do it. + (disco_append_message): Redo the append methods to no longer + return the UID, since we're no longer doing the logging from here. + + * providers/imap/camel-imap-store.c (imap_connect_online, + imap_connect_offline): Create a CamelDiscoDiary. + (imap_disconnect_offline): And free it. + + * providers/imap/camel-imap-folder.c (camel_imap_folder_selected): + If RESYNCING, don't do any sort of checking that the remote folder + matches the summary, beyond making sure that the UIDVALIDITY is + correct. + (imap_rescan): Add a missing camel_folder_summary_info_free when + removing a UID from the summary. + (imap_expunge_uids_offline): Implement. Fairly simple. + (imap_expunge_uids_resyncing): Implement. If the store supports + UIDPLUS, we can just use imap_expunge_uids_online. If not, we need + to temporarily undelete any messages marked deleted on the server + that aren't supposed to get expunged. + (imap_append_offline): Implement, using cache and summary + operations, and triggering the folder_changed event by hand. + (imap_append_resyncing): Implement. Redo imap_append_online a bit + in the process to make them able to share more code. + (imap_copy_offline): Implement. + (imap_copy_online): Move parts of this out into a helper. + (imap_copy_resyncing): Implement. In most cases this is just like + imap_copy_online, but if you are copying a message that was itself + copied or appended into the folder, and the server doesn't do + UIDPLUS, it will be necessary to replace at least part of the copy + operation with one or more appends. + + * providers/imap/camel-imap-command.c (imap_read_response): Don't + record the current folder in the response when in RESYNCING mode. + (This means that EXISTS and EXPUNGE responses won't be processed, + which is needed because the summary may not match the folder at + this point.) + (imap_read_response): On error, call + camel_imap_response_free_without_processing, not + camel_imap_response_free. + + * providers/imap/camel-imap-utils.c (imap_uid_array_to_set): Make + this work better when operating on UIDs that aren't in the summary. + + * providers/imap/camel-imap-summary.c + (camel_imap_summary_add_offline): New routine used by + imap_append_offline and imap_copy_offline to create new summary + entries. + 2001-05-28 Jeffrey Stedfast * camel-mime-utils.c (header_set_param): Use g_strcasecmp() diff --git a/camel/Makefile.am b/camel/Makefile.am index ad0688f8cb..8284d35abc 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -24,6 +24,7 @@ libcamel_la_SOURCES = \ camel-cipher-context.c \ camel-data-wrapper.c \ camel-digest-folder.c \ + camel-disco-diary.c \ camel-disco-folder.c \ camel-disco-store.c \ camel-exception.c \ @@ -104,6 +105,7 @@ libcamelinclude_HEADERS = \ camel-cipher-context.h \ camel-data-wrapper.h \ camel-digest-folder.h \ + camel-disco-diary.h \ camel-disco-folder.h \ camel-disco-store.h \ camel-exception-list.def \ 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 + * + * 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 +#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 + + +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); +} diff --git a/camel/camel-disco-diary.h b/camel/camel-disco-diary.h new file mode 100644 index 0000000000..8d2e8708e4 --- /dev/null +++ b/camel/camel-disco-diary.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * camel-disco-diary.h: class for logging disconnected operation + * + * Authors: Dan Winship + * + * Copyright 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 + */ + +#ifndef CAMEL_DISCO_DIARY_H +#define CAMEL_DISCO_DIARY_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include "camel-object.h" +#include +#include + +#define CAMEL_DISCO_DIARY_TYPE (camel_disco_diary_get_type ()) +#define CAMEL_DISCO_DIARY(obj) (CAMEL_CHECK_CAST((obj), CAMEL_DISCO_DIARY_TYPE, CamelDiscoDiary)) +#define CAMEL_DISCO_DIARY_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_DISCO_DIARY_TYPE, CamelDiscoDiaryClass)) +#define CAMEL_IS_DISCO_DIARY(o) (CAMEL_CHECK_TYPE((o), CAMEL_DISCO_DIARY_TYPE)) + +typedef enum { + CAMEL_DISCO_DIARY_END = 0, + + CAMEL_DISCO_DIARY_FOLDER_EXPUNGE, + CAMEL_DISCO_DIARY_FOLDER_APPEND, + CAMEL_DISCO_DIARY_FOLDER_MOVE, + CAMEL_DISCO_DIARY_FOLDER_COPY +} CamelDiscoDiaryAction; + +typedef enum { + CAMEL_DISCO_DIARY_ARG_NONE = 0, + + CAMEL_DISCO_DIARY_ARG_FOLDER, + CAMEL_DISCO_DIARY_ARG_UID, + CAMEL_DISCO_DIARY_ARG_UID_LIST +} CamelDiscoDiaryArgType; + +struct _CamelDiscoDiary { + CamelObject parent_object; + + CamelDiscoStore *store; + FILE *file; + GHashTable *folders, *uidmap; +}; + +typedef struct { + CamelObjectClass parent_class; + +} CamelDiscoDiaryClass; + + +/* public methods */ +CamelDiscoDiary *camel_disco_diary_new (CamelDiscoStore *store, + const char *filename, + CamelException *ex); + +gboolean camel_disco_diary_empty (CamelDiscoDiary *diary); + +void camel_disco_diary_log (CamelDiscoDiary *diary, + CamelDiscoDiaryAction action, + ...); +void camel_disco_diary_replay (CamelDiscoDiary *diary, + CamelException *ex); + +/* Temporary->Permanent UID map stuff */ +void camel_disco_diary_uidmap_add (CamelDiscoDiary *diary, + const char *old_uid, + const char *new_uid); +const char *camel_disco_diary_uidmap_lookup (CamelDiscoDiary *diary, + const char *uid); + +/* Standard Camel function */ +CamelType camel_disco_diary_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_DISCO_DIARY_H */ diff --git a/camel/camel-disco-folder.c b/camel/camel-disco-folder.c index b090bd6517..f3581bce19 100644 --- a/camel/camel-disco-folder.c +++ b/camel/camel-disco-folder.c @@ -116,6 +116,10 @@ disco_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) case CAMEL_DISCO_STORE_OFFLINE: CDF_CLASS (folder)->sync_offline (folder, ex); break; + + case CAMEL_DISCO_STORE_RESYNCING: + CDF_CLASS (folder)->sync_resyncing (folder, ex); + break; } } @@ -134,13 +138,10 @@ disco_expunge_uids (CamelFolder *folder, GPtrArray *uids, CamelException *ex) case CAMEL_DISCO_STORE_OFFLINE: CDF_CLASS (folder)->expunge_uids_offline (folder, uids, ex); -#ifdef NOTYET - if (!camel_exception_is_set (ex)) { - camel_disco_diary_log (disco->diary, - CAMEL_DISCO_DIARY_FOLDER_EXPUNGE, - folder, uids); - } -#endif + break; + + case CAMEL_DISCO_STORE_RESYNCING: + CDF_CLASS (folder)->expunge_uids_resyncing (folder, uids, ex); break; } } @@ -173,25 +174,20 @@ disco_append_message (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex) { CamelDiscoStore *disco = CAMEL_DISCO_STORE (folder->parent_store); - char *uid; switch (camel_disco_store_status (disco)) { case CAMEL_DISCO_STORE_ONLINE: - uid = CDF_CLASS (folder)->append_online (folder, message, info, ex); + CDF_CLASS (folder)->append_online (folder, message, info, ex); break; case CAMEL_DISCO_STORE_OFFLINE: - uid = CDF_CLASS (folder)->append_offline (folder, message, info, ex); -#ifdef NOTYET - if (uid) { - camel_disco_diary_log (disco->diary, - CAMEL_DISCO_DIARY_FOLDER_APPEND, - folder, uid); - } -#endif + CDF_CLASS (folder)->append_offline (folder, message, info, ex); + break; + + case CAMEL_DISCO_STORE_RESYNCING: + CDF_CLASS (folder)->append_resyncing (folder, message, info, ex); break; } - g_free (uid); } static void @@ -207,13 +203,10 @@ disco_copy_messages_to (CamelFolder *source, GPtrArray *uids, case CAMEL_DISCO_STORE_OFFLINE: CDF_CLASS (source)->copy_offline (source, uids, destination, ex); -#ifdef NOTYET - if (!camel_exception_is_set (ex)) { - camel_disco_diary_log (disco->diary, - CAMEL_DISCO_DIARY_FOLDER_COPY, - source, destination, uids); - } -#endif + break; + + case CAMEL_DISCO_STORE_RESYNCING: + CDF_CLASS (source)->copy_resyncing (source, uids, destination, ex); break; } } @@ -231,13 +224,10 @@ disco_move_messages_to (CamelFolder *source, GPtrArray *uids, case CAMEL_DISCO_STORE_OFFLINE: CDF_CLASS (source)->move_offline (source, uids, destination, ex); -#ifdef NOTYET - if (!camel_exception_is_set (ex)) { - camel_disco_diary_log (disco->diary, - CAMEL_DISCO_DIARY_FOLDER_MOVE, - source, destination, uids); - } -#endif + break; + + case CAMEL_DISCO_STORE_RESYNCING: + CDF_CLASS (source)->move_resyncing (source, uids, destination, ex); break; } } diff --git a/camel/camel-disco-folder.h b/camel/camel-disco-folder.h index cdca5a3b52..7b0186ffc3 100644 --- a/camel/camel-disco-folder.h +++ b/camel/camel-disco-folder.h @@ -45,34 +45,45 @@ struct _CamelDiscoFolder { typedef struct { CamelFolderClass parent_class; - void (*refresh_info_online) (CamelFolder *folder, CamelException *ex); - - void (*sync_online) (CamelFolder *folder, CamelException *ex); - void (*sync_offline) (CamelFolder *folder, CamelException *ex); - - void (*expunge_uids_online) (CamelFolder *folder, GPtrArray *uids, - CamelException *ex); - void (*expunge_uids_offline) (CamelFolder *folder, GPtrArray *uids, - CamelException *ex); - - char * (*append_online) (CamelFolder *folder, - CamelMimeMessage *message, - const CamelMessageInfo *info, - CamelException *ex); - char * (*append_offline) (CamelFolder *folder, - CamelMimeMessage *message, - const CamelMessageInfo *info, - CamelException *ex); + void (*refresh_info_online) (CamelFolder *folder, CamelException *ex); + + void (*sync_online) (CamelFolder *folder, CamelException *ex); + void (*sync_offline) (CamelFolder *folder, CamelException *ex); + void (*sync_resyncing) (CamelFolder *folder, CamelException *ex); + + void (*expunge_uids_online) (CamelFolder *folder, GPtrArray *uids, + CamelException *ex); + void (*expunge_uids_offline) (CamelFolder *folder, GPtrArray *uids, + CamelException *ex); + void (*expunge_uids_resyncing) (CamelFolder *folder, GPtrArray *uids, + CamelException *ex); + + void (*append_online) (CamelFolder *folder, + CamelMimeMessage *message, + const CamelMessageInfo *info, + CamelException *ex); + void (*append_offline) (CamelFolder *folder, + CamelMimeMessage *message, + const CamelMessageInfo *info, + CamelException *ex); + void (*append_resyncing) (CamelFolder *folder, + CamelMimeMessage *message, + const CamelMessageInfo *info, + CamelException *ex); void (*copy_online) (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); void (*copy_offline) (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); + void (*copy_resyncing) (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex); void (*move_online) (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); void (*move_offline) (CamelFolder *source, GPtrArray *uids, CamelFolder *destination, CamelException *ex); + void (*move_resyncing) (CamelFolder *source, GPtrArray *uids, + CamelFolder *destination, CamelException *ex); void (*cache_message) (CamelDiscoFolder *disco_folder, const char *uid, CamelException *ex); diff --git a/camel/camel-disco-store.c b/camel/camel-disco-store.c index ae51b54f0f..ebe26a1908 100644 --- a/camel/camel-disco-store.c +++ b/camel/camel-disco-store.c @@ -27,12 +27,17 @@ #endif #include "camel-disco-store.h" +#include "camel-disco-diary.h" #include "camel-exception.h" +#include "camel-session.h" #define CDS_CLASS(o) (CAMEL_DISCO_STORE_CLASS (CAMEL_OBJECT_GET_CLASS (o))) static CamelRemoteStoreClass *remote_store_class = NULL; +static void disco_construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex); static gboolean disco_connect (CamelService *service, CamelException *ex); static gboolean disco_disconnect (CamelService *service, gboolean clean, CamelException *ex); static CamelFolder *disco_get_folder (CamelStore *store, const char *name, @@ -60,6 +65,7 @@ camel_disco_store_class_init (CamelDiscoStoreClass *camel_disco_store_class) camel_disco_store_class->can_work_offline = can_work_offline; /* virtual method overload */ + camel_service_class->construct = disco_construct; camel_service_class->connect = disco_connect; camel_service_class->disconnect = disco_disconnect; @@ -67,16 +73,6 @@ camel_disco_store_class_init (CamelDiscoStoreClass *camel_disco_store_class) camel_store_class->get_folder_info = disco_get_folder_info; } -static void -camel_disco_store_init (CamelDiscoStore *store) -{ - /* Hack */ - if (getenv ("CAMEL_OFFLINE")) - store->status = CAMEL_DISCO_STORE_OFFLINE; - else - store->status = CAMEL_DISCO_STORE_ONLINE; -} - CamelType camel_disco_store_get_type (void) { @@ -89,13 +85,28 @@ camel_disco_store_get_type (void) sizeof (CamelDiscoStoreClass), (CamelObjectClassInitFunc) camel_disco_store_class_init, NULL, - (CamelObjectInitFunc) camel_disco_store_init, + NULL, NULL); } return camel_disco_store_type; } +static void +disco_construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex) +{ + CamelDiscoStore *disco = CAMEL_DISCO_STORE (service); + + CAMEL_SERVICE_CLASS (remote_store_class)->construct (service, session, provider, url, ex); + if (camel_exception_is_set (ex)) + return; + + disco->status = camel_session_is_online (session) ? + CAMEL_DISCO_STORE_ONLINE : CAMEL_DISCO_STORE_OFFLINE; +} + static gboolean disco_connect (CamelService *service, CamelException *ex) { @@ -106,14 +117,29 @@ disco_connect (CamelService *service, CamelException *ex) switch (camel_disco_store_status (store)) { case CAMEL_DISCO_STORE_ONLINE: - return CDS_CLASS (service)->connect_online (service, ex); + case CAMEL_DISCO_STORE_RESYNCING: + if (!CDS_CLASS (service)->connect_online (service, ex)) + return FALSE; + if (camel_disco_diary_empty (store->diary)) + return TRUE; + + /* Need to resync */ + store->status = CAMEL_DISCO_STORE_RESYNCING; + camel_disco_diary_replay (store->diary, ex); + store->status = CAMEL_DISCO_STORE_ONLINE; + if (camel_exception_is_set (ex)) + return FALSE; + + if (!camel_service_disconnect (service, TRUE, ex)) + return FALSE; + return camel_service_connect (service, ex); case CAMEL_DISCO_STORE_OFFLINE: return CDS_CLASS (service)->connect_offline (service, ex); } - /* Not reached */ - return TRUE; + g_assert_not_reached (); + return FALSE; } static gboolean @@ -123,6 +149,7 @@ disco_disconnect (CamelService *service, gboolean clean, CamelException *ex) switch (camel_disco_store_status (store)) { case CAMEL_DISCO_STORE_ONLINE: + case CAMEL_DISCO_STORE_RESYNCING: if (!CDS_CLASS (service)->disconnect_online (service, clean, ex)) return FALSE; break; @@ -131,6 +158,7 @@ disco_disconnect (CamelService *service, gboolean clean, CamelException *ex) if (!CDS_CLASS (service)->disconnect_offline (service, clean, ex)) return FALSE; break; + } return CAMEL_SERVICE_CLASS (remote_store_class)->disconnect (service, clean, ex); @@ -148,9 +176,12 @@ disco_get_folder (CamelStore *store, const char *name, case CAMEL_DISCO_STORE_OFFLINE: return CDS_CLASS (store)->get_folder_offline (store, name, flags, ex); + + case CAMEL_DISCO_STORE_RESYNCING: + return CDS_CLASS (store)->get_folder_resyncing (store, name, flags, ex); } - /* Not reached */ + g_assert_not_reached (); return NULL; } @@ -173,9 +204,12 @@ disco_get_folder_info (CamelStore *store, const char *top, } return CDS_CLASS (store)->get_folder_info_offline (store, top, flags, ex); + + case CAMEL_DISCO_STORE_RESYNCING: + return CDS_CLASS (store)->get_folder_info_resyncing (store, top, flags, ex); } - /* Not reached */ + g_assert_not_reached (); return NULL; } diff --git a/camel/camel-disco-store.h b/camel/camel-disco-store.h index cb40cf4590..73ba677c34 100644 --- a/camel/camel-disco-store.h +++ b/camel/camel-disco-store.h @@ -42,15 +42,14 @@ extern "C" { typedef enum { CAMEL_DISCO_STORE_ONLINE, CAMEL_DISCO_STORE_OFFLINE, -#ifdef NOTYET CAMEL_DISCO_STORE_RESYNCING -#endif } CamelDiscoStoreStatus; struct _CamelDiscoStore { CamelRemoteStore parent_object; CamelDiscoStoreStatus status; + CamelDiscoDiary *diary; }; @@ -62,6 +61,7 @@ typedef struct { CamelException *); gboolean (*can_work_offline) (CamelDiscoStore *); + gboolean (*connect_online) (CamelService *, CamelException *); gboolean (*connect_offline) (CamelService *, @@ -80,16 +80,24 @@ typedef struct { const char *name, guint32 flags, CamelException *ex); - - CamelFolderInfo * (*get_folder_info_online) (CamelStore *store, - const char *top, - guint32 flags, - CamelException *ex); - CamelFolderInfo * (*get_folder_info_offline) (CamelStore *store, - const char *top, + CamelFolder * (*get_folder_resyncing) (CamelStore *store, + const char *name, guint32 flags, CamelException *ex); + CamelFolderInfo * (*get_folder_info_online) (CamelStore *store, + const char *top, + guint32 flags, + CamelException *ex); + CamelFolderInfo * (*get_folder_info_offline) (CamelStore *store, + const char *top, + guint32 flags, + CamelException *ex); + CamelFolderInfo * (*get_folder_info_resyncing) (CamelStore *store, + const char *top, + guint32 flags, + CamelException *ex); + } CamelDiscoStoreClass; @@ -103,6 +111,7 @@ void camel_disco_store_set_status (CamelDiscoStore *, CamelException *); gboolean camel_disco_store_can_work_offline (CamelDiscoStore *); + /* Convenience functions */ gboolean camel_disco_store_check_online (CamelDiscoStore *store, CamelException *ex); diff --git a/camel/camel-types.h b/camel/camel-types.h index dbcce56550..4a146b5668 100644 --- a/camel/camel-types.h +++ b/camel/camel-types.h @@ -29,6 +29,7 @@ extern "C" { typedef struct _CamelAddress CamelAddress; typedef struct _header_content_type CamelContentType; +typedef struct _CamelDiscoDiary CamelDiscoDiary; typedef struct _CamelDiscoFolder CamelDiscoFolder; typedef struct _CamelDiscoStore CamelDiscoStore; typedef struct _CamelDataWrapper CamelDataWrapper; diff --git a/camel/camel.h b/camel/camel.h index e8708a70c9..0e3f9dcc9c 100644 --- a/camel/camel.h +++ b/camel/camel.h @@ -37,6 +37,7 @@ extern "C" { #include #include #include +#include #include #include #include -- cgit v1.2.3