1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-disco-diary.c: class for a disconnected operation log */
5 * Authors: Dan Winship <danw@ximian.com>
7 * Copyright (C) 2001 Ximian, Inc.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU Lesser General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
28 #define __USE_LARGEFILE 1
33 #include <glib/gstdio.h>
34 #include <glib/gi18n-lib.h>
36 #include "camel-disco-diary.h"
37 #include "camel-disco-folder.h"
38 #include "camel-disco-store.h"
39 #include "camel-exception.h"
40 #include "camel-file-utils.h"
41 #include "camel-folder.h"
42 #include "camel-operation.h"
43 #include "camel-session.h"
44 #include "camel-store.h"
49 camel_disco_diary_class_init (CamelDiscoDiaryClass *camel_disco_diary_class)
51 /* virtual method definition */
55 camel_disco_diary_init (CamelDiscoDiary *diary)
57 diary->folders = g_hash_table_new (g_str_hash, g_str_equal);
58 diary->uidmap = g_hash_table_new (g_str_hash, g_str_equal);
62 unref_folder (gpointer key, gpointer value, gpointer data)
64 camel_object_unref (value);
68 free_uid (gpointer key, gpointer value, gpointer data)
75 camel_disco_diary_finalize (CamelDiscoDiary *diary)
80 g_hash_table_foreach (diary->folders, unref_folder, NULL);
81 g_hash_table_destroy (diary->folders);
84 g_hash_table_foreach (diary->uidmap, free_uid, NULL);
85 g_hash_table_destroy (diary->uidmap);
90 camel_disco_diary_get_type (void)
92 static CamelType camel_disco_diary_type = CAMEL_INVALID_TYPE;
94 if (camel_disco_diary_type == CAMEL_INVALID_TYPE) {
95 camel_disco_diary_type = camel_type_register (
96 CAMEL_OBJECT_TYPE, "CamelDiscoDiary",
97 sizeof (CamelDiscoDiary),
98 sizeof (CamelDiscoDiaryClass),
99 (CamelObjectClassInitFunc) camel_disco_diary_class_init,
101 (CamelObjectInitFunc) camel_disco_diary_init,
102 (CamelObjectFinalizeFunc) camel_disco_diary_finalize);
105 return camel_disco_diary_type;
110 diary_encode_uids (CamelDiscoDiary *diary, GPtrArray *uids)
114 status = camel_file_util_encode_uint32 (diary->file, uids->len);
115 for (i = 0; status != -1 && i < uids->len; i++)
116 status = camel_file_util_encode_string (diary->file, uids->pdata[i]);
121 camel_disco_diary_log (CamelDiscoDiary *diary, CamelDiscoDiaryAction action,
127 d(printf("diary log: %s\n", diary->file?"ok":"no file!"));
129 /* You may already be a loser. */
130 if (!diary && !diary->file)
133 status = camel_file_util_encode_uint32 (diary->file, action);
137 va_start (ap, action);
139 case CAMEL_DISCO_DIARY_FOLDER_EXPUNGE:
141 CamelFolder *folder = va_arg (ap, CamelFolder *);
142 GPtrArray *uids = va_arg (ap, GPtrArray *);
144 d(printf(" folder expunge '%s'\n", folder->full_name));
146 status = camel_file_util_encode_string (diary->file, folder->full_name);
148 status = diary_encode_uids (diary, uids);
152 case CAMEL_DISCO_DIARY_FOLDER_APPEND:
154 CamelFolder *folder = va_arg (ap, CamelFolder *);
155 char *uid = va_arg (ap, char *);
157 d(printf(" folder append '%s'\n", folder->full_name));
159 status = camel_file_util_encode_string (diary->file, folder->full_name);
161 status = camel_file_util_encode_string (diary->file, uid);
165 case CAMEL_DISCO_DIARY_FOLDER_TRANSFER:
167 CamelFolder *source = va_arg (ap, CamelFolder *);
168 CamelFolder *destination = va_arg (ap, CamelFolder *);
169 GPtrArray *uids = va_arg (ap, GPtrArray *);
170 gboolean delete_originals = va_arg (ap, gboolean);
172 d(printf(" folder transfer '%s' to '%s'\n", source->full_name, destination->full_name));
174 status = camel_file_util_encode_string (diary->file, source->full_name);
177 status = camel_file_util_encode_string (diary->file, destination->full_name);
180 status = diary_encode_uids (diary, uids);
183 status = camel_file_util_encode_uint32 (diary->file, delete_originals);
188 g_assert_not_reached ();
198 msg = g_strdup_printf (_("Could not write log entry: %s\n"
199 "Further operations on this server "
200 "will not be replayed when you\n"
201 "reconnect to the network."),
203 camel_session_alert_user (camel_service_get_session (CAMEL_SERVICE (diary->store)),
204 CAMEL_SESSION_ALERT_ERROR,
208 fclose (diary->file);
214 free_uids (GPtrArray *array)
217 g_free (array->pdata[array->len]);
218 g_ptr_array_free (array, TRUE);
222 diary_decode_uids (CamelDiscoDiary *diary)
228 if (camel_file_util_decode_uint32 (diary->file, &i) == -1)
230 uids = g_ptr_array_new ();
232 if (camel_file_util_decode_string (diary->file, &uid) == -1) {
236 g_ptr_array_add (uids, uid);
243 diary_decode_folder (CamelDiscoDiary *diary)
248 if (camel_file_util_decode_string (diary->file, &name) == -1)
250 folder = g_hash_table_lookup (diary->folders, name);
255 camel_exception_init (&ex);
256 folder = camel_store_get_folder (CAMEL_STORE (diary->store),
259 g_hash_table_insert (diary->folders, name, folder);
261 msg = g_strdup_printf (_("Could not open `%s':\n%s\nChanges made to this folder will not be resynchronized."),
262 name, camel_exception_get_description (&ex));
263 camel_exception_clear (&ex);
264 camel_session_alert_user (camel_service_get_session (CAMEL_SERVICE (diary->store)),
265 CAMEL_SESSION_ALERT_WARNING,
276 close_folder (gpointer name, gpointer folder, gpointer data)
279 camel_folder_sync (folder, FALSE, NULL);
280 camel_object_unref (folder);
284 camel_disco_diary_replay (CamelDiscoDiary *diary, CamelException *ex)
290 d(printf("disco diary replay\n"));
292 fseek (diary->file, 0, SEEK_END);
293 size = ftell (diary->file);
294 g_return_if_fail (size != 0);
295 rewind (diary->file);
297 camel_operation_start (NULL, _("Resynchronizing with server"));
298 while (!camel_exception_is_set (ex)) {
299 pc = ftell (diary->file) / size;
300 camel_operation_progress (NULL, pc * 100);
302 if (camel_file_util_decode_uint32 (diary->file, &action) == -1)
304 if (action == CAMEL_DISCO_DIARY_END)
308 case CAMEL_DISCO_DIARY_FOLDER_EXPUNGE:
313 folder = diary_decode_folder (diary);
314 uids = diary_decode_uids (diary);
319 camel_disco_folder_expunge_uids (folder, uids, ex);
324 case CAMEL_DISCO_DIARY_FOLDER_APPEND:
328 CamelMimeMessage *message;
329 CamelMessageInfo *info;
331 folder = diary_decode_folder (diary);
332 if (camel_file_util_decode_string (diary->file, &uid) == -1)
340 message = camel_folder_get_message (folder, uid, NULL);
342 /* The message was appended and then deleted. */
346 info = camel_folder_get_message_info (folder, uid);
348 camel_folder_append_message (folder, message, info, &ret_uid, ex);
349 camel_folder_free_message_info (folder, info);
352 camel_disco_diary_uidmap_add (diary, uid, ret_uid);
360 case CAMEL_DISCO_DIARY_FOLDER_TRANSFER:
362 CamelFolder *source, *destination;
363 GPtrArray *uids, *ret_uids;
364 guint32 delete_originals;
367 source = diary_decode_folder (diary);
368 destination = diary_decode_folder (diary);
369 uids = diary_decode_uids (diary);
372 if (camel_file_util_decode_uint32 (diary->file, &delete_originals) == -1)
375 if (!source || !destination) {
380 camel_folder_transfer_messages_to (source, uids, destination, &ret_uids, delete_originals, ex);
383 for (i = 0; i < uids->len; i++) {
384 if (!ret_uids->pdata[i])
386 camel_disco_diary_uidmap_add (diary, uids->pdata[i], ret_uids->pdata[i]);
387 g_free (ret_uids->pdata[i]);
389 g_ptr_array_free (ret_uids, TRUE);
399 camel_operation_end (NULL);
402 g_hash_table_foreach (diary->folders, close_folder, diary);
403 g_hash_table_destroy (diary->folders);
404 diary->folders = NULL;
406 /* Truncate the log */
407 ftruncate (fileno (diary->file), 0);
411 camel_disco_diary_new (CamelDiscoStore *store, const char *filename, CamelException *ex)
413 CamelDiscoDiary *diary;
415 g_return_val_if_fail (CAMEL_IS_DISCO_STORE (store), NULL);
416 g_return_val_if_fail (filename != NULL, NULL);
418 diary = CAMEL_DISCO_DIARY (camel_object_new (CAMEL_DISCO_DIARY_TYPE));
419 diary->store = store;
421 d(printf("diary log file '%s'\n", filename));
423 /* Note that the linux man page says:
425 a+ Open for reading and appending (writing at end of file). The
426 file is created if it does not exist. The stream is positioned
427 at the end of the file.
428 However, c99 (which glibc uses?) says:
429 a+ append; open or create text file for update, writing at
432 So we must seek ourselves.
435 diary->file = g_fopen (filename, "a+b");
437 camel_object_unref (diary);
438 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
439 "Could not open journal file: %s",
444 fseek(diary->file, 0, SEEK_END);
446 d(printf(" is at %ld\n", ftell(diary->file)));
452 camel_disco_diary_empty (CamelDiscoDiary *diary)
454 return ftell (diary->file) == 0;
458 camel_disco_diary_uidmap_add (CamelDiscoDiary *diary, const char *old_uid,
461 g_hash_table_insert (diary->uidmap, g_strdup (old_uid),
466 camel_disco_diary_uidmap_lookup (CamelDiscoDiary *diary, const char *uid)
468 return g_hash_table_lookup (diary->uidmap, uid);