aboutsummaryrefslogblamecommitdiffstats
path: root/camel/camel-folder-pt-proxy.c
blob: 6aa53400004a37ad77c008c4cac2f7431cc04643 (plain) (tree)





















                                                                           
 

 



                                  


                   











































                                                                                                        



                                                                   











































                                                                                                


                                                             
                                                          

                                                           
 














                                                                         
                                                                    















                                                                                                                   
                                                                                         


                                                                         

                                                                




                                                                        
                                
 









                                                     
            
                                                                         
 




                                          
                                                                 

                                                       
                                                                          
                                                                                         

                                   

                                                      
                                                      
         


 
 
 




















                                                                         
                                                 




                                                                                 




















                                                                                           
        



                                                                           





                                                             




                                                











                                                           
           
                                                         

                                         
                                         

                            

                                                                                      

                                                                

                                                       






                                                          

























                                                                                            
 








                                                            

 













































































































                                                                                                             

                                            





                                 
                                       
 

                                          
 
                                                     
 


                                                                                    
                       



                                                   







                                                                          
                    



                                                             
                                           


                                                                    
 
                             
                                                           
                                           
                                                  

                                           
                                          

                           
                                                     




        
                                 
 


                                 
















                                                                  



                                                     


                                                                          
 









                                                     




 

                                  






















































































































































































































































                                                                                           
/* -*- 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 <Bertrand.Guiheneuf@aful.org> .
 *
 * 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-log.h"
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

/* 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);
static void _open (CamelFolder *folder, CamelFolderOpenMode mode);
static void _close (CamelFolder *folder, gboolean expunge);
static void _set_name (CamelFolder *folder, const gchar *name);
static const gchar *_get_name (CamelFolder *folder);
static const gchar *_get_full_name (CamelFolder *folder);
static gboolean _can_hold_folders (CamelFolder *folder);
static gboolean _can_hold_messages(CamelFolder *folder);
static gboolean _exists (CamelFolder  *folder);
static gboolean _is_open (CamelFolder *folder);
static CamelFolder *_get_folder (CamelFolder *folder, const gchar *folder_name);
static gboolean _create (CamelFolder *folder);
static gboolean _delete (CamelFolder *folder, gboolean recurse);
static gboolean _delete_messages (CamelFolder *folder);
static CamelFolder *_get_parent_folder (CamelFolder *folder);
static CamelStore *_get_parent_store (CamelFolder *folder);
static CamelFolderOpenMode _get_mode (CamelFolder *folder);
static GList *_list_subfolders (CamelFolder *folder);
static void _expunge (CamelFolder *folder);
static CamelMimeMessage *_get_message (CamelFolder *folder, gint number);
static gint _get_message_count (CamelFolder *folder);
static gint _append_message (CamelFolder *folder, CamelMimeMessage *message);
static const GList *_list_permanent_flags (CamelFolder *folder);
static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder);

static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message);
static CamelMimeMessage *_get_message_by_uid (CamelFolder *folder, const gchar *uid);
static GList *_get_uid_list  (CamelFolder *folder);


static void _finalize (GtkObject *object);

/* for the proxy watch */
static gboolean _thread_notification_catch (GIOChannel *source,
                        GIOCondition condition,
                        gpointer data);

static void
camel_folder_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_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_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_proxy_class_init,
            (GtkObjectInitFunc) camel_folder_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;
} _InitStoreParam;

static void
_async_init_with_store (gpointer param)
{
    _InitStoreParam *init_store_param;
    CamelFolder *folder;

    init_store_param =  (_InitStoreParam *)param;

    folder = init_store_param->folder;

    CF_CLASS (folder)->init_with_store (folder, init_store_param->parent_store);
    g_free (param);
    
    /* tell the main thread we are completed */
    _notify_availability (folder, 'a');

}


static void 
_init_with_store (CamelFolder *folder, CamelStore *parent_store)
{
    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;
} _OpenFolderParam;

static void  
_async_open (gpointer param)
{
    _OpenFolderParam *open_folder_param;
    CamelFolder *folder;
    
    open_folder_param = (_OpenFolderParam *)param;
    
    folder =open_folder_param->folder;
    
    CF_CLASS (folder)->open (folder, open_folder_param->mode);
    g_free (param);
    _notify_availability (folder, 'a');

}

static void
_open (CamelFolder *folder, CamelFolderOpenMode mode)
{
    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 */
static void
_close (CamelFolder *folder, gboolean expunge)
{
    if (expunge) camel_folder_expunge (folder, FALSE);
    folder->open_state = FOLDER_CLOSE;
}




static void
_set_name (CamelFolder *folder, const gchar *name)
{

}



static const gchar *
_get_name (CamelFolder *folder)
{
    return folder->name;
}




static const gchar *
_get_full_name (CamelFolder *folder)
{
    return folder->full_name;
}




static gboolean
_can_hold_folders (CamelFolder *folder)
{
    return folder->can_hold_folders;
}




static gboolean
_can_hold_messages (CamelFolder *folder)
{
    return folder->can_hold_messages;
}



static gboolean
_exists (CamelFolder *folder)
{
    return FALSE;
}




static gboolean
_is_open (CamelFolder *folder)
{
    return (folder->open_state == FOLDER_OPEN);
} 





static CamelFolder *
_get_folder (CamelFolder *folder, const gchar *folder_name)
{

    return NULL;
}






static gboolean
_create(CamelFolder *folder)
{
    
    return FALSE;
}








static gboolean
_delete (CamelFolder *folder, gboolean recurse)
{
    return FALSE;
}







static gboolean 
_delete_messages (CamelFolder *folder)
{
    return TRUE;
}






static CamelFolder *
_get_parent_folder (CamelFolder *folder)
{
    return folder->parent_folder;
}





static CamelStore *
_get_parent_store (CamelFolder *folder)
{
    return folder->parent_store;
}




static CamelFolderOpenMode
_get_mode (CamelFolder *folder)
{
    return folder->open_mode;
}




static GList *
_list_subfolders (CamelFolder *folder)
{
    return NULL;
}




static void
_expunge (CamelFolder *folder)
{

}




static CamelMimeMessage *
_get_message (CamelFolder *folder, gint number)
{
    return NULL;
}





static gint
_get_message_count (CamelFolder *folder)
{
    return -1;
}




static gint
_append_message (CamelFolder *folder, CamelMimeMessage *message)
{
    return -1;
}



static const GList *
_list_permanent_flags (CamelFolder *folder)
{
    return folder->permanent_flags;
}



static void
_copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder)
{
    camel_folder_append_message (dest_folder, message);
}






/* UIDs stuff */


static const gchar *
_get_message_uid (CamelFolder *folder, CamelMimeMessage *message)
{
    return NULL;
}


/* the next two func are left there temporarily */
static const gchar *
_get_message_uid_by_number (CamelFolder *folder, gint message_number)
{
    return NULL;
}



static CamelMimeMessage *
_get_message_by_uid (CamelFolder *folder, const gchar *uid)
{
    return NULL;
}


static GList *
_get_uid_list  (CamelFolder *folder)
{
    return NULL;
}


/* **** */