/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* camel-folder-pt-proxy.c : proxy folder using posix threads */
/*
*
* Author :
* Bertrand Guiheneuf <bertrand@helixcode.com>
*
* Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
*
* 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
*/
#include <config.h>
#include "camel-folder-pt-proxy.h"
#include "camel-marshal-utils.h"
#include "camel-exception.h"
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
static CamelFolderClass *parent_class = NULL;
/* Returns the class for CamelFolderPtProxy and CamelFolder objects */
#define CFPP_CLASS(so) CAMEL_FOLDER_PT_PROXY_CLASS (GTK_OBJECT(so)->klass)
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT(so)->klass)
enum CamelFolderFunc {
CAMEL_FOLDER_OPEN,
CAMEL_FOLDER_CLOSE,
CAMEL_FOLDER__LAST_FUNC
};
static CamelFuncDef _camel_func_def [CAMEL_FOLDER__LAST_FUNC];
static void _init (CamelFolder *folder, CamelStore *parent_store,
CamelFolder *parent_folder, const gchar *name,
gchar separator, CamelException *ex);
static void _open_async (CamelFolder *folder,
CamelFolderOpenMode mode,
CamelFolderAsyncCallback callback,
gpointer user_data,
CamelException *ex);
static void _close_async (CamelFolder *folder,
gboolean expunge,
CamelFolderAsyncCallback callback,
gpointer user_data,
CamelException *ex);
static void _open (CamelFolder *folder,
CamelFolderOpenMode mode,
CamelException *ex);
static void _close (CamelFolder *folder,
gboolean expunge,
CamelException *ex);
static void _set_name (CamelFolder *folder,
const gchar *name,
CamelException *ex);
static const gchar *_get_name (CamelFolder *folder, CamelException *ex);
static const gchar *_get_full_name (CamelFolder *folder, CamelException *ex);
static gboolean _can_hold_folders (CamelFolder *folder);
static gboolean _can_hold_messages(CamelFolder *folder);
static gboolean _exists (CamelFolder *folder, CamelException *ex);
static gboolean _is_open (CamelFolder *folder);
static CamelFolder *_get_subfolder (CamelFolder *folder, const gchar *folder_name, CamelException *ex);
static gboolean _create (CamelFolder *folder, CamelException *ex);
static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex);
static gboolean _delete_messages (CamelFolder *folder, CamelException *ex);
static CamelFolder *_get_parent_folder (CamelFolder *folder, CamelException *ex);
static CamelStore *_get_parent_store (CamelFolder *folder, CamelException *ex);
static CamelFolderOpenMode _get_mode (CamelFolder *folder, CamelException *ex);
static GList *_list_subfolders (CamelFolder *folder, CamelException *ex);
static GList *_expunge (CamelFolder *folder, CamelException *ex);
static CamelMimeMessage *_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex);
static gint _get_message_count (CamelFolder *folder, CamelException *ex);
static void _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
static const GList *_list_permanent_flags (CamelFolder *folder, CamelException *ex);
static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder, CamelException *ex);
static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
static CamelMimeMessage *_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex);
static GList *_get_uid_list (CamelFolder *folder, CamelException *ex);
static void _finalize (GtkObject *object);
static void
camel_folder_pt_proxy_class_init (CamelFolderPtProxyClass *camel_folder_pt_proxy_class)
{
GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (camel_folder_pt_proxy_class);
CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_folder_pt_proxy_class);
CamelFolderPtProxyClass *proxy_class = camel_folder_pt_proxy_class;
parent_class = gtk_type_class (camel_folder_get_type ());
/* virtual method definition */
camel_folder_class->init = _init;
camel_folder_class->open = _open;
camel_folder_class->close = _close;
#ifdef FOLDER_ASYNC_TEST
camel_folder_class->open_async = _open_async;
camel_folder_class->close_async = _close_async;
#endif
camel_folder_class->get_name = _get_name;
camel_folder_class->can_hold_folders = _can_hold_folders;
camel_folder_class->can_hold_messages = _can_hold_messages;
camel_folder_class->exists = _exists;
camel_folder_class->is_open = _is_open;
camel_folder_class->get_subfolder = _get_subfolder;
camel_folder_class->create = _create;
camel_folder_class->delete = _delete;
camel_folder_class->delete_messages = _delete_messages;
camel_folder_class->get_parent_folder = _get_parent_folder;
camel_folder_class->get_parent_store = _get_parent_store;
camel_folder_class->get_mode = _get_mode;
camel_folder_class->list_subfolders = _list_subfolders;
camel_folder_class->expunge = _expunge;
camel_folder_class->get_message_by_number = _get_message_by_number;
camel_folder_class->get_message_count = _get_message_count;
camel_folder_class->append_message = _append_message;
camel_folder_class->list_permanent_flags = _list_permanent_flags;
camel_folder_class->copy_message_to = _copy_message_to;
camel_folder_class->get_message_uid = _get_message_uid;
camel_folder_class->get_message_by_uid = _get_message_by_uid;
camel_folder_class->get_uid_list = _get_uid_list;
/* virtual method overload */
gtk_object_class->finalize = _finalize;
/* function definition for proxying */
proxy_class->open_func_def =
camel_func_def_new (camel_marshal_NONE__POINTER_INT_POINTER_POINTER,
4,
GTK_TYPE_POINTER,
GTK_TYPE_INT,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
proxy_class->open_cb_def =
camel_func_def_new (camel_marshal_NONE__POINTER_POINTER_POINTER,
3,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
proxy_class->close_func_def =
camel_func_def_new (camel_marshal_NONE__POINTER_BOOL_POINTER_POINTER,
4,
GTK_TYPE_POINTER,
GTK_TYPE_BOOL,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
proxy_class->close_cb_def =
camel_func_def_new (camel_marshal_NONE__POINTER_POINTER_POINTER,
3,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
proxy_class->set_name_func_def =
camel_func_def_new (camel_marshal_NONE__POINTER_BOOL_POINTER_POINTER,
4,
GTK_TYPE_POINTER,
GTK_TYPE_BOOL,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
proxy_class->set_name_cb_def =
camel_func_def_new (camel_marshal_NONE__POINTER_POINTER_POINTER,
3,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER,
GTK_TYPE_POINTER);
}
static void
camel_folder_pt_proxy_init (CamelFolderPtProxy *folder_pt_proxy)
{
folder_pt_proxy->thread_ex = camel_exception_new ();
folder_pt_proxy->pud = g_new (_ProxyCbUserData, 1);
}
GtkType
camel_folder_pt_proxy_get_type (void)
{
static GtkType camel_folder_pt_proxy_type = 0;
if (!camel_folder_pt_proxy_type) {
GtkTypeInfo camel_folder_pt_proxy_info =
{
"CamelFolderPtProxy",
sizeof (CamelFolderPtProxy),
sizeof (CamelFolderPtProxyClass),
(GtkClassInitFunc) camel_folder_pt_proxy_class_init,
(GtkObjectInitFunc) camel_folder_pt_proxy_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
camel_folder_pt_proxy_type = gtk_type_unique (camel_folder_get_type (), &camel_folder_pt_proxy_info);
}
return camel_folder_pt_proxy_type;
}
static void
_finalize (GtkObject *object)
{
CamelFolder *camel_folder = CAMEL_FOLDER (object);
CamelFolderPtProxy *camel_folder_pt_proxy = CAMEL_FOLDER_PT_PROXY (camel_folder);
camel_exception_free (camel_folder_pt_proxy->thread_ex);
g_free (camel_folder_pt_proxy->pud);
GTK_OBJECT_CLASS (parent_class)->finalize (object);
}
/*********/
/**** Operations implementation ****/
static gpointer
_proxy_cb_user_data (_ProxyCbUserData *pud,
CamelFolderAsyncCallback real_callback,
CamelFolderPtProxy *proxy_folder,
CamelException *ex,
gpointer real_user_data)
{
pud->real_callback = real_callback;
pud->proxy_folder = proxy_folder;
pud->ex = ex;
pud->real_user_data = real_user_data;
return (gpointer)pud;
}
/* ******** */
/* thread->init implementation */
static void
_init (CamelFolder *folder, CamelStore *parent_store,
CamelFolder *parent_folder, const gchar *name,
gchar separator, CamelException *ex)
{
parent_class->init (folder, parent_store, parent_folder,
name, separator, ex);
if (ex->id != CAMEL_EXCEPTION_NONE)
return;
#warning use proxy store here
CF_CLASS (folder)->init (CAMEL_FOLDER_PT_PROXY (folder)->real_folder,
parent_store, parent_folder, name,
separator, ex);
}
/* a little bit of explanation for the folder_class->open
* method implementation :
*
* the proxy object "open" method is called by the client
* program in the main thread. This method creates a
* CamelOp object containing all the necessary informations
* to call the corresponding "open" method on the real
* folder object in the child thread. This CamelOp object
* is thus pushed in a queue in the main thread (see the
* CamelThreadProxy structure for more details).
* The operations in this queue are executed one by one
* in a child thread.
* Once the "open" method of the real object is finished,
* it calls a callback. This callback is not the one supplied
* by the client object. Instead, the _folder_open_cb()
* function is called (in the child thread) which pushes
* the real callback function in another operation queue.
* The real callback is then called in the main thread.
*/
/* folder->open implementation */
/*
* proxy callback. Called in the child thread by the
* real folder "open" method when it is completed
*/
static void
_folder_open_cb (CamelFolder *folder,
gpointer user_data,
CamelException *ex)
{
CamelOp *cb;
_ProxyCbUserData *pud;
CamelFuncDef *cb_def;
g_print ("%s: %s(): Bailing. This function is incredibly"
" broken; the \"*pud\" pointer isn't allocated"
" memory anywhere.\n");
return;
/* transfer the exception information from "ex" to the
* client supplied exception (kept in pud->ex) */
camel_exception_xfer (pud->ex, ex);
/* create an operation which will call the real client
* supplied callback in the main thread */
cb_def = CAMEL_FOLDER_PT_PROXY_CLASS(pud->proxy_folder)->open_cb_def;
cb = camel_marshal_create_op (cb_def,
pud->real_callback,
pud->proxy_folder,
pud->real_user_data,
pud->ex);
camel_thread_proxy_push_cb (pud->proxy_folder->proxy_object, cb);
}
static void
_open_async (CamelFolder *folder,
CamelFolderOpenMode mode,
CamelFolderAsyncCallback callback,
gpointer user_data,
CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
CamelOp *op;
CamelFuncDef *func_def;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
/* create an operation corresponding to the "open"
* method of the real object. The operation definition
* is common to all instances of the CamelFolderPtProxy
* class so it is contained in the CamelFolderPtProxyClass
* structure. */
func_def = CAMEL_FOLDER_PT_PROXY_CLASS(proxy_folder)->open_func_def;
if (callback)
op = camel_marshal_create_op (func_def,
CAMEL_FOLDER_CLASS (proxy_folder->real_folder)->open_async,
proxy_folder->real_folder,
mode,
_folder_open_cb,
_proxy_cb_user_data (proxy_folder->pud, callback, proxy_folder, ex, user_data),
proxy_folder->thread_ex);
else
op = camel_marshal_create_op (func_def,
CAMEL_FOLDER_CLASS (proxy_folder->real_folder)->open_async,
proxy_folder->real_folder,
mode,
NULL,
NULL,
NULL);
/* push the operation in the operation queue. This operation
* will be executed in a child thread but only one operation
* will be executed at a time, so that folder implementations
* don't have to be MultiThread safe. */
camel_thread_proxy_push_op (proxy_folder->proxy_object, op);
}
static void _open (CamelFolder *folder,
CamelFolderOpenMode mode,
CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
CF_CLASS (proxy_folder->real_folder)->
open (proxy_folder->real_folder, mode, ex);
}
/* folder->close implementation */
static void
_folder_close_cb (CamelFolder *folder,
gpointer user_data,
CamelException *ex)
{
CamelOp *cb;
_ProxyCbUserData *pud;
CamelFuncDef *cb_def;
g_print ("%s: %s(): Bailing. This function is incredibly"
" broken; the \"*pud\" pointer isn't allocated"
" memory anywhere.\n");
return;
camel_exception_xfer (pud->ex, ex);
cb_def = CAMEL_FOLDER_PT_PROXY_CLASS(pud->proxy_folder)->close_cb_def;
cb = camel_marshal_create_op (cb_def,
pud->real_callback,
pud->proxy_folder,
pud->real_user_data,
pud->ex);
camel_thread_proxy_push_cb (pud->proxy_folder->proxy_object, cb);
}
static void
_close_async (CamelFolder *folder,
gboolean expunge,
CamelFolderAsyncCallback callback,
gpointer user_data,
CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
CamelOp *op;
CamelFuncDef *func_def;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
func_def = CAMEL_FOLDER_PT_PROXY_CLASS(proxy_folder)->close_func_def;
if (callback)
op = camel_marshal_create_op (func_def,
CAMEL_FOLDER_CLASS (proxy_folder->real_folder)->close_async,
proxy_folder->real_folder,
expunge,
_folder_close_cb,
_proxy_cb_user_data (proxy_folder->pud, callback, proxy_folder, ex, user_data),
proxy_folder->thread_ex);
else
op = camel_marshal_create_op (func_def,
CAMEL_FOLDER_CLASS (proxy_folder->real_folder)->close_async,
proxy_folder->real_folder,
expunge,
NULL,
NULL,
NULL);
camel_thread_proxy_push_op (proxy_folder->proxy_object, op);
}
static void _close (CamelFolder *folder,
gboolean expunge,
CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
CF_CLASS (proxy_folder->real_folder)->
close (proxy_folder->real_folder, expunge, ex);
}
/* folder->get_name implementation */
/* this one is not executed in a thread */
static const gchar *
_get_name (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_name (proxy_folder->real_folder);
}
/* folder->get_full_name implementation */
/* this one is not executed in a thread */
static const gchar *
_get_full_name (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_full_name (proxy_folder->real_folder);
}
static gboolean
_can_hold_folders (CamelFolder *folder)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
can_hold_folders (proxy_folder->real_folder);
}
static gboolean
_can_hold_messages (CamelFolder *folder)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
can_hold_messages (proxy_folder->real_folder);
}
static gboolean
_exists (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
exists (proxy_folder->real_folder, ex);
}
static gboolean
_is_open (CamelFolder *folder)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
is_open (proxy_folder->real_folder);
}
static CamelFolder *
_get_subfolder (CamelFolder *folder, const gchar *folder_name, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_subfolder (proxy_folder->real_folder, folder_name, ex);
}
static gboolean
_create(CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
create (proxy_folder->real_folder, ex);
}
static gboolean
_delete (CamelFolder *folder, gboolean recurse, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
delete (proxy_folder->real_folder, recurse, ex);
}
static gboolean
_delete_messages (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
delete_messages (proxy_folder->real_folder, ex);
}
static CamelFolder *
_get_parent_folder (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
#warning return proxy parent folder if any
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_parent_folder (proxy_folder->real_folder, ex);
}
static CamelStore *
_get_parent_store (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_parent_store (proxy_folder->real_folder, ex);
}
static CamelFolderOpenMode
_get_mode (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_mode (proxy_folder->real_folder, ex);
}
static GList *
_list_subfolders (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
list_subfolders (proxy_folder->real_folder, ex);
}
static GList *
_expunge (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
expunge (proxy_folder->real_folder, ex);
}
static CamelMimeMessage *
_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_message_by_number (proxy_folder->real_folder, number, ex);
}
static gint
_get_message_count (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_message_count (proxy_folder->real_folder, ex);
}
static void
_append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
CF_CLASS (proxy_folder->real_folder)->
append_message (proxy_folder->real_folder, message, ex);
}
static const GList *
_list_permanent_flags (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
list_permanent_flags (proxy_folder->real_folder, ex);
}
static void
_copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
CF_CLASS (proxy_folder->real_folder)->
copy_message_to (proxy_folder->real_folder, message, dest_folder, ex);
}
/* UIDs stuff */
static const gchar *
_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_message_uid (proxy_folder->real_folder, message, ex);
}
/* the next two func are left there temporarily */
#if 0
static const gchar *
_get_message_uid_by_number (CamelFolder *folder, gint message_number, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_message_uid_by_number (proxy_folder->real_folder, message_number, ex);
}
#endif
static CamelMimeMessage *
_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_message_by_uid (proxy_folder->real_folder, uid, ex);
}
static GList *
_get_uid_list (CamelFolder *folder, CamelException *ex)
{
CamelFolderPtProxy *proxy_folder;
proxy_folder = CAMEL_FOLDER_PT_PROXY (folder);
return CF_CLASS (proxy_folder->real_folder)->
get_uid_list (proxy_folder->real_folder, ex);
}
/* **** */