/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-folder-pt-proxy.c : proxy folder using posix threads */ /* * * Copyright (C) 1999 Bertrand Guiheneuf . * * 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 #include "camel-folder-pt-proxy.h" #include "camel-log.h" #include #include #include #include /* needed for proper casts of async funcs when * calling pthreads_create */ typedef void * (*thread_call_func) (void *); 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) static void _init_with_store (CamelFolder *folder, CamelStore *parent_store, 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, CamelException *ex); static gboolean _can_hold_messages(CamelFolder *folder, CamelException *ex); static gboolean _exists (CamelFolder *folder, CamelException *ex); static gboolean _is_open (CamelFolder *folder, CamelException *ex); static CamelFolder *_get_folder (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 void _expunge (CamelFolder *folder, CamelException *ex); static CamelMimeMessage *_get_message (CamelFolder *folder, gint number, CamelException *ex); static gint _get_message_count (CamelFolder *folder, CamelException *ex); static gint _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); /* for the proxy watch */ static gboolean _thread_notification_catch (GIOChannel *source, GIOCondition condition, gpointer data); 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); parent_class = gtk_type_class (camel_folder_get_type ()); /* virtual method definition */ camel_folder_class->init_with_store = _init_with_store; camel_folder_class->open = _open; camel_folder_class->close = _close; camel_folder_class->set_name = _set_name; 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_folder = _get_folder; 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 = _get_message; 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; 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; } static void camel_folder_pt_proxy_init (CamelFolderPtProxy *folder_pt_proxy) { folder_pt_proxy->op_queue = camel_op_queue_new (); folder_pt_proxy->signal_data_cond = g_cond_new(); folder_pt_proxy->signal_data_mutex = g_mutex_new(); } 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 (gtk_object_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); GList *message_node; CAMEL_LOG_FULL_DEBUG ("Entering CamelFolderPtProxy::finalize\n"); g_cond_free (camel_folder_pt_proxy->signal_data_cond); g_mutex_free (camel_folder_pt_proxy->signal_data_mutex); GTK_OBJECT_CLASS (parent_class)->finalize (object); CAMEL_LOG_FULL_DEBUG ("Leaving CamelFolderPtProxy::finalize\n"); } /* generic operation handling */ /** * _op_exec_or_plan_for_exec: * @proxy_folder: * @op: * * if no thread is currently running, executes * op, otherwise push the operation in the operation * queue. **/ static void _op_exec_or_plan_for_exec (CamelFolderPtProxy *proxy_folder, CamelOp *op) { CamelOpQueue *op_queue; pthread_t thread; op_queue = proxy_folder->op_queue; if (camel_op_queue_get_service_availability (op_queue)) { /* no thread is currently running, run * the operation directly */ camel_op_queue_set_service_availability (op_queue, FALSE); pthread_create (&thread, NULL , (thread_call_func)(op->func), op->param); camel_op_free (op); } else { /* a child thread is already running, * push the operation in the queue */ camel_op_queue_push_op (op_queue, op); } } /** * _maybe_run_next_op: run next operation in queue, if any * @proxy_folder: * * **/ static void _maybe_run_next_op (CamelFolderPtProxy *proxy_folder) { CamelOp *op; CamelOpQueue *op_queue; pthread_t thread; op_queue = proxy_folder->op_queue; /* get the next pending operation */ op = camel_op_queue_pop_op (op_queue); if (!op) { camel_op_queue_set_service_availability (op_queue, TRUE); return; } /* run the operation in a child thread */ pthread_create (&thread, NULL , (thread_call_func)(op->func), op->param); camel_op_free (op); } /** * _init_notify_system: set the notify channel up * @proxy_folder: * * called once to set the notification channel **/ static void _init_notify_system (CamelFolderPtProxy *proxy_folder) { int filedes[2]; /* set up the notification channel */ if (!pipe (filedes)) { CAMEL_LOG_WARNING ("could not create pipe in for camel_folder_proxy_init"); CAMEL_LOG_FULL_DEBUG ("Full error message : %s\n", strerror(errno)); return; } proxy_folder->pipe_client_fd = filedes [0]; proxy_folder->pipe_server_fd = filedes [1]; proxy_folder->notify_source = g_io_channel_unix_new (filedes [0]); /* the _thread_notification_catch function * will be called in the main thread when the * child thread writes some data in the channel */ g_io_add_watch (proxy_folder->notify_source, G_IO_IN, _thread_notification_catch, proxy_folder); } /** * notify_availability: notify thread completion * @folder: server folder (in the child thread) * @op_name: operation name * * called by child thread (real folder) to notify the main * thread (folder proxy) something is available for him. * What this thing is depends on @op_name: * * 'a' : thread available. That means the thread is ready * to process an operation. * 's' : a signal is available. Used by the signal proxy. * */ static void _notify_availability (CamelFolder *folder, gchar op_name) { GIOChannel *notification_channel; CamelFolderPtProxy *proxy_folder; guint bytes_written; proxy_folder = (CamelFolderPtProxy *)gtk_object_get_data (GTK_OBJECT (folder), "proxy_folder"); notification_channel = proxy_folder->notify_source; do { /* the write operation will trigger the * watch on the main thread side */ g_io_channel_write (notification_channel, &op_name, 1, &bytes_written); } while (bytes_written == 1); } /* signal proxying */ static void _signal_marshaller_server_side (GtkObject *object, gpointer data, guint n_args, GtkArg *args) { CamelFolder *folder; CamelFolderPtProxy *proxy_folder; guint signal_id; folder = CAMEL_FOLDER (object); proxy_folder = CAMEL_FOLDER_PT_PROXY (gtk_object_get_data (object, "proxy_folder")); signal_id = (guint)data; g_assert (proxy_folder); g_mutex_lock (proxy_folder->signal_data_mutex); /* we are going to wait for the main client thread * to have emitted the last signal we asked him * to proxy. */ while (proxy_folder->signal_data.args) g_cond_wait (proxy_folder->signal_data_cond, proxy_folder->signal_data_mutex); proxy_folder->signal_data.signal_id = signal_id; proxy_folder->signal_data.args = args; g_mutex_unlock (proxy_folder->signal_data_mutex); /* tell the main thread there is a signal pending */ _notify_availability (folder, 's'); } static void _signal_marshaller_client_side (CamelFolderPtProxy *proxy_folder) { g_mutex_lock (proxy_folder->signal_data_mutex); g_assert (proxy_folder->signal_data.args); /* emit the pending signal */ gtk_signal_emitv (GTK_OBJECT (proxy_folder), proxy_folder->signal_data.signal_id, proxy_folder->signal_data.args); proxy_folder->signal_data.args = NULL; /* if waiting for the signal to be treated, * awake the client thread up */ g_cond_signal (proxy_folder->signal_data_cond); g_mutex_unlock (proxy_folder->signal_data_mutex); } _init_signals_proxy (CamelFolderPtProxy *proxy_folder) { CamelFolder *real_folder; GtkType camel_folder_type; guint i; char *signal_to_proxy[] = { NULL }; camel_folder_type = CAMEL_FOLDER_TYPE; real_folder = proxy_folder->real_folder; for (i=0; signal_to_proxy[i]; i++) { /* conect the signal to the signal marshaller * user_data is the signal id */ gtk_signal_connect_full (GTK_OBJECT (real_folder), signal_to_proxy[i], NULL, _signal_marshaller_server_side, (gpointer)gtk_signal_lookup (signal_to_proxy[i], camel_folder_type), NULL, TRUE, FALSE); } } /**** catch notification from the child thread ****/ /** * _thread_notification_catch: call by glib loop when data is available on the thread io channel * @source: * @condition: * @data: * * called by watch set on the IO channel * * Return value: TRUE because we don't want the watch to be removed **/ static gboolean _thread_notification_catch (GIOChannel *source, GIOCondition condition, gpointer data) { CamelFolderPtProxy *proxy_folder = (CamelFolderPtProxy *)data; gchar op_name; guint bytes_read; GIOError error; error = g_io_channel_read (source, &op_name, 1, &bytes_read); switch (op_name) { case 'a': /* the thread is OK for a new operation */ _maybe_run_next_op (proxy_folder); break; case 's': /* there is a pending signal to proxy */ _signal_marshaller_client_side (proxy_folder); break; } /* do not remove the io watch */ return TRUE; } /*********/ /**** Operations implementation ****/ /* * the _async prefixed operations are * executed in a child thread. * When completed, they must call * notify_availability () in order to * tell the main thread it can process * a new operation. * */ /* folder->init_with_store implementation */ typedef struct { CamelFolder *folder; CamelStore *parent_store; CamelException *ex; } _InitStoreParam; static void _async_init_with_store (gpointer param) { _InitStoreParam *init_store_param; CamelFolder *folder; CamelException *ex; init_store_param = (_InitStoreParam *)param; folder = init_store_param->folder; CF_CLASS (folder)->init_with_store (folder, init_store_param->parent_store, NULL); g_free (param); /* tell the main thread we are completed */ _notify_availability (folder, 'a'); } static void _init_with_store (CamelFolder *folder, CamelStore *parent_store, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); _InitStoreParam *param; CamelOp *op; #warning Notify io_channel initialization should be elsewhere /* it can not be in camel_folder_proxy_init * because of the pipe error handling */ _init_notify_system (proxy_folder); gtk_object_set_data (GTK_OBJECT (proxy_folder->real_folder), "proxy_folder", proxy_folder); op = camel_op_new (); /* param will be freed in _async_init_with_store */ param = g_new (_InitStoreParam, 1); param->folder = proxy_folder->real_folder; param->parent_store = parent_store; op->func = _async_init_with_store; op->param = param; _op_exec_or_plan_for_exec (proxy_folder, op); } /* folder->open implementation */ typedef struct { CamelFolder *folder; CamelFolderOpenMode mode; CamelException *ex; } _OpenFolderParam; static void _async_open (gpointer param) { _OpenFolderParam *open_folder_param; CamelFolder *folder; CamelException *ex; open_folder_param = (_OpenFolderParam *)param; folder = open_folder_param->folder; CF_CLASS (folder)->open (folder, open_folder_param->mode, NULL); g_free (param); _notify_availability (folder, 'a'); } static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); _OpenFolderParam *param; CamelOp *op; op = camel_op_new (); param = g_new (_OpenFolderParam, 1); param->folder = proxy_folder->real_folder; param->mode = mode; op->func = _async_open; op->param = param; _op_exec_or_plan_for_exec (proxy_folder, op); } /* folder->close implementation */ typedef struct { CamelFolder *folder; gboolean expunge; CamelException *ex; } _CloseFolderParam; static void _async_close (gpointer param) { _CloseFolderParam *close_folder_param; CamelFolder *folder; CamelException *ex; close_folder_param = (_CloseFolderParam *)param; folder = close_folder_param->folder; CF_CLASS (folder)->close (folder, close_folder_param->expunge, NULL); g_free (param); _notify_availability (folder, 'a'); } static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); _CloseFolderParam *param; CamelOp *op; op = camel_op_new (); param = g_new (_CloseFolderParam, 1); param->folder = proxy_folder->real_folder; param->expunge = expunge; op->func = _async_close; op->param = param; _op_exec_or_plan_for_exec (proxy_folder, op); } /* folder->set_name implementation */ typedef struct { CamelFolder *folder; const gchar *name; CamelException *ex; } _SetNameFolderParam; static void _async_set_name (gpointer param) { _SetNameFolderParam *set_name_folder_param; CamelFolder *folder; CamelException *ex; set_name_folder_param = (_SetNameFolderParam *)param; folder = set_name_folder_param->folder; CF_CLASS (folder)->set_name (folder, set_name_folder_param->name, NULL); g_free (param); _notify_availability (folder, 'a'); } static void _set_name (CamelFolder *folder, const gchar *name, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); _SetNameFolderParam *param; CamelOp *op; op = camel_op_new (); param = g_new (_SetNameFolderParam, 1); param->folder = proxy_folder->real_folder; param->name = name; op->func = _async_set_name; op->param = param; _op_exec_or_plan_for_exec (proxy_folder, op); } /* folder->get_name implementation */ /* this one i not executed in a thread */ static const gchar * _get_name (CamelFolder *folder, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); return CF_CLASS (proxy_folder->real_folder)-> get_name (proxy_folder->real_folder, ex); } static const gchar * _get_full_name (CamelFolder *folder, CamelException *ex) { CamelFolderPtProxy *proxy_folder = CAMEL_FOLDER_PT_PROXY (folder); return CF_CLASS (proxy_folder->real_folder)-> get_full_name (proxy_folder->real_folder, ex); } static gboolean _can_hold_folders (CamelFolder *folder, CamelException *ex) { return folder->can_hold_folders; } static gboolean _can_hold_messages (CamelFolder *folder, CamelException *ex) { return folder->can_hold_messages; } static gboolean _exists (CamelFolder *folder, CamelException *ex) { return FALSE; } static gboolean _is_open (CamelFolder *folder, CamelException *ex) { return (folder->open_state == FOLDER_OPEN); } static CamelFolder * _get_folder (CamelFolder *folder, const gchar *folder_name, CamelException *ex) { return NULL; } static gboolean _create(CamelFolder *folder, CamelException *ex) { return FALSE; } static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex) { return FALSE; } static gboolean _delete_messages (CamelFolder *folder, CamelException *ex) { return TRUE; } static CamelFolder * _get_parent_folder (CamelFolder *folder, CamelException *ex) { return folder->parent_folder; } static CamelStore * _get_parent_store (CamelFolder *folder, CamelException *ex) { return folder->parent_store; } static CamelFolderOpenMode _get_mode (CamelFolder *folder, CamelException *ex) { return folder->open_mode; } static GList * _list_subfolders (CamelFolder *folder, CamelException *ex) { return NULL; } static void _expunge (CamelFolder *folder, CamelException *ex) { } static CamelMimeMessage * _get_message (CamelFolder *folder, gint number, CamelException *ex) { return NULL; } static gint _get_message_count (CamelFolder *folder, CamelException *ex) { return -1; } static gint _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex) { return -1; } static const GList * _list_permanent_flags (CamelFolder *folder, CamelException *ex) { return folder->permanent_flags; } static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder, CamelException *ex) { camel_folder_append_message (dest_folder, message, ex); } /* UIDs stuff */ static const gchar * _get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex) { return NULL; } /* the next two func are left there temporarily */ static const gchar * _get_message_uid_by_number (CamelFolder *folder, gint message_number, CamelException *ex) { return NULL; } static CamelMimeMessage * _get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex) { return NULL; } static GList * _get_uid_list (CamelFolder *folder, CamelException *ex) { return NULL; } /* **** */