
2001-08-24 Jeffrey Stedfast <fejj@ximian.com> * camel-disco-diary.c (camel_disco_diary_replay): Use fseek instead of fseeko since we want to be portable and use ftell rather than ftello for the same reason. (camel_disco_diary_empty): Same here. svn path=/trunk/; revision=12469
418 lines
9.8 KiB
C
418 lines
9.8 KiB
C
/* -*- 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->uidmap, 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;
|
|
|
|
fseek (diary->file, 0, SEEK_END);
|
|
size = ftell (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 = ftell (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 ftell (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);
|
|
}
|