aboutsummaryrefslogblamecommitdiffstats
path: root/addressbook/backend/ebook/e-book.c
blob: b551c8727b3501b4d08549677fa4ee8f311bc004 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                           
 
                   


                    
                   
 
                   








                                                
                           

                            
                                        
                             
 
                                  
 
                                                                       
 


















































                                                                                                         
              


                              
                 

                      


                              
                                


                             



                                        
                                             
 
                                                            
 

                                          
 


                            

                   
 
                               
                           

  







                                                          
 

           
 
 
 
                   
 

                           
 
                                          
 

                                                 
 
                                    
 

                  
 








                                                  

 
           
                            
 




                                         
 





                                                                 
 
                                      

 


                             
 



                                    
 
 
 

















                                             
 
                                        
 

                                                                                             
 
                                         
 





                                                                                            
 




                                                                    
         
 
                                                                               
 
                                      
 
                                     
 
                                           
 
                                   
 


                                                                                                               
 










                                                                                

         




                                                                
 






                                                           

 
           


                                                 

                    
 


                                                 

                         

                                                                            
         
 
                                 
 

                               
 
                                        
 
                                   

 















                                                                
 

                           
                             
                        
 

                                                                                             
 






                                                                                               

         





                                                                    
 
                                                                               
 
                                      
 
                                     
 
                                           
 
                                   
 


                                                                                                                  
 


                                              
 
                                               
 




                                                                                   
         
 
                                   
 


                                                                
 


                                                           
 

                                                              
 
                                            

 














                                                                    
 

                           
                             
 

                                                                                    
 






                                                                                              

         




                                                                    
 
                                      
 
                                     
 
                                           
 








                                                                                         
 

                                           


                                                                                        
         
 
 
                                   
 









                                                                


           


                                                          


                    
                                  

                         

                                                                                     

         
                                 
 

                            
 
                                        
 
                                   

 













                                                                    
 


                             
 

                                                                                    
 
                                         
 




                                                                                                    

         




                                                                    

         
                                      
 

































                                                                                              


           


                                                                                


                    
                                  

                         

                                                                                           

         
                                 
 

                                
 
                                        
 

                                   
 
 

   





















                                                                
 



                                                                                    
 
                                         
 




                                                                                           
         
 




                                                                    
         
 
                                      
 
                                     
 
                                           


                                   





                                                                                  

                                              


                                               
                                           



                                                                                      



                                   








                                                                
 
 
 















                                                            
 

                           
                             
 









                                                                                       


                             











                                                                    

                                   



                                                                                                        

                                              
 

                                               
                                           



                                                                                

         

                                   


                                                                
 

                                   
 
                                       
 












                                                  
 


                                                                            

         
                                 
 

                              
 


                                        

 













                                              
 

                    
 

                                                                                    
 
                                         
 




                                                                                        
         
 




                                                                    

         
                                         
 
                                               
 
                                                        
 
                  


   














                                                                        
 
                                                         
                             



                           
 

                                                                                    
 






                                                                                         

         












                                                                    

                                   












                                                                                                 
                                              
 








                                                                                    

                                   


                                                                
 
                                
 
                                       
 
                                            

 


















                                                                                       
 






                                                          
 


                                                                                          
 
                                         
 





                                                                                       
 





                                                                    
 
                                      
 































                                                                                                                  
 
                                           





                                                                                 
         





                                                                
 





                                            
 
 



                                                                                    
 
 
                    
 
                                                   
 











                                                                              

                                                       


                                        

 















                                           

                             


                           
 


                                                                                          
 






                                                                                      

         





                                                                    
 













                                                                                                    

                                              


                                               
                                           
 






                                                                                    

                                   









                                                                

 






























                                                                             

                             

                           
 











                                                                                          
 




                                                                    

         
                                      
 







                                                                                            

                                              


                                               
                                           
 






                                                                                

                                   


                                                                
 

                                
 
                                       
 
                                            

 



                                                      
 
 
                    
 




                                                                             

         





                                        
 

                                   

    
                                            
 


                                               
 
                                                 
                                

         

                                  
 
 
 




                                             
 




                                                                        

         


                                 
 



                                        

   

















                                                                      
 


                           
                             
 
                                         
 




                                                                                
         
 


                                    
 
                                           
 
                                                                                               

                                              


                                           
                                           



                                                                                     


                                   
 















                                                                                

 






















                                                                     
 
        

                              
 




                                                                                    
 
                                         
 



                                                                                

                             
 





                                                                    
 
                                      
 
                                     
 
                                           
 
                                   
 

                                                                              
 





















                                                                               

 


                                            
 
                    
 
                                            
 




                                                                       

         
                                 
 
                            
 





                                        
 












































                                                                                            
         
 
 
 
 












                                                                                                              
                                              

                                                                    
         
 

                                   







                                                                   
 


                    
 

   
                   
   
 

                                                            
 




                                                                                                                              
 







                                                         
 


                                                                                         


                             







                                                                                            
 










                                                                                                     

         








                                                                                        
 





                                                                                                    
 




                                       
 
                         

 
        



                                              
 



                                                                       
 

                                                                                    
 






                                                                                                                  


                             
 
                                                    
 







                                                                                   

                             

                                                                                                   
 

                                         
 




                                                                          
 
                                              
 
                                             
 
                                           
 


                                                                                                                                        
 
                                                      
                                                       
 


                                                   
 


                                                                      
 




                                                                                                        
 
                                                       
 


                                                   
 
                                           
 


                                                                        
 
                                        
 

                                                                      
 




                                                    

         


                                                                   
 














                                                                                                        

 
        

                                              
 


                       
 



                                                                
 
                          
        


                                                      
 
                  

 

                            
 

                               
 






                                               
 
                                           
 



                                                                             
 
                                                                                                           
 





                                                                               
 

                                                 
 
                                 
 
                                           
         
 
                               

 


                                                 
 
                                                                       
 


                                                                  
 

                     
 




                                    
 
 
 
 

                                                                  
 
                         
 

                                                         


                             


                                  
 
                                                                           
 



                                                           
         
 




                                                    
 






                                   
 

                                                             
 

 



                                                             
 


                                                                        
 
                              
 




                                                            
 
                                                        
 
                     
 


                                       

         









                                                                       
 


























                                                                                     
         

                                             
 
 
 




                                                

 
 


                         
                                                          
                                                       
                                      
                                                           


           
                                

                                                  
 


                                      
 





                                                                                                         

                                                                
 





                                                                                             
 


                                                           
                 
                
                                           
 
                                           
                                                                                                        



                                                                   

                                         
                                         
 


                                    
 

                                                                




                                     
                                                            
 
                                                        
 
                                          







                                                                            
 
                                       








                                                                         

 


                   
     

                      
                              

                     
                                  
                                            







                                                            

                  
                                                                                 



                    
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

#include <config.h>

#include <pthread.h>

#include <string.h>

#include "e-book.h"
#include "e-vcard.h"

#include <bonobo-activation/bonobo-activation.h>

#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-main.h>

#include <libgnome/gnome-i18n.h>

#include "e-book-marshal.h"
#include "e-book-listener.h"
#include "addressbook.h"
#include "e-util/e-component-listener.h"
#include "e-util/e-msgport.h"

static GObjectClass *parent_class;

#define CARDSERVER_OAF_ID "OAFIID:GNOME_Evolution_Wombat_ServerFactory"

#define e_return_error_if_fail(expr,error_code) G_STMT_START{       \
     if G_LIKELY(expr) { } else                     \
       {                                \
     g_log (G_LOG_DOMAIN,                       \
        G_LOG_LEVEL_CRITICAL,                   \
        "file %s: line %d (%s): assertion `%s' failed",     \
        __FILE__,                       \
        __LINE__,                       \
        __PRETTY_FUNCTION__,                    \
        #expr);                         \
     g_set_error (error, E_BOOK_ERROR, (error_code),                \
        "file %s: line %d (%s): assertion `%s' failed",     \
        __FILE__,                       \
        __LINE__,                       \
        __PRETTY_FUNCTION__,                    \
        #expr);                         \
     return FALSE;                          \
       };               }G_STMT_END

/* XXX we need a better error message here */
#define E_BOOK_CHECK_STATUS(status,error) G_STMT_START{         \
    if ((status) == E_BOOK_ERROR_OK) {              \
        return TRUE;                        \
    }                               \
    else {                              \
        g_set_error ((error), E_BOOK_ERROR, (status), "EBookStatus returned %d", (status)); \
        return FALSE;                       \
    }               }G_STMT_END

enum {
    OPEN_PROGRESS,
    WRITABLE_STATUS,
    BACKEND_DIED,
    LAST_SIGNAL
};

static guint e_book_signals [LAST_SIGNAL];

typedef struct {
    EMutex *mutex;
    pthread_cond_t cond;
    EBookStatus status;

    char *id;
    GList *list;
    EContact *contact;

    EBookView *view;
    EBookViewListener *listener;
} EBookOp;

typedef enum {
    E_BOOK_URI_NOT_LOADED,
    E_BOOK_URI_LOADING,
    E_BOOK_URI_LOADED
} EBookLoadState;

struct _EBookPrivate {
    GList *book_factories;
    GList *iter;

    /* cached capabilites */
    char *cap;
    gboolean cap_queried;

    /* cached writable status */
    gboolean writable;

    EBookListener         *listener;
    EComponentListener    *comp_listener;

    GNOME_Evolution_Addressbook_Book         corba_book;

    EBookLoadState         load_state;


    EBookOp *current_op;

    EMutex *mutex;

    gchar *uri;

    gulong listener_signal;
    gulong died_signal;
};


/* Error quark */
GQuark
e_book_error_quark (void)
{
  static GQuark q = 0;
  if (q == 0)
    q = g_quark_from_static_string ("e-book-error-quark");

  return q;
}



/* EBookOp calls */

static EBookOp*
e_book_new_op (EBook *book)
{
    EBookOp *op = g_new0 (EBookOp, 1);

    op->mutex = e_mutex_new (E_MUTEX_SIMPLE);
    pthread_cond_init (&op->cond, 0);

    book->priv->current_op = op;

    return op;
}

static EBookOp*
e_book_get_op (EBook *book)
{
    if (!book->priv->current_op) {
        g_warning ("unexpected response");
        return NULL;
    }
        
    return book->priv->current_op;
}

static void
e_book_op_free (EBookOp *op)
{
    /* XXX more stuff here */
    pthread_cond_destroy (&op->cond);
    e_mutex_destroy (op->mutex);
    g_free (op);
}

static void
e_book_op_remove (EBook *book,
          EBookOp *op)
{
    if (book->priv->current_op != op)
        g_warning ("cannot remove op, it's not current");

    book->priv->current_op = NULL;
}

static void
e_book_clear_op (EBook *book,
         EBookOp *op)
{
    e_book_op_remove (book, op);
    e_mutex_unlock (op->mutex);
    e_book_op_free (op);
}



/**
 * e_book_add_card:
 * @book: an #EBook
 * @contact: an #EContact
 *
 * adds @contact to @book.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_add_contact (EBook           *book,
            EContact        *contact,
            GError         **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;
    char *vcard_str;

    printf ("e_book_add_contact\n");

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (contact && E_IS_CONTACT (contact), E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_add_contact called on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling e_book_response_add_contact */
    GNOME_Evolution_Addressbook_Book_addContact (book->priv->corba_book,
                             (const GNOME_Evolution_Addressbook_VCard) vcard_str, &ev);

    g_free (vcard_str);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::addContact call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    e_contact_set (contact, E_CONTACT_UID, our_op->id);
    g_free (our_op->id);

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_add_contact (EBook       *book,
                 EBookStatus  status,
                 char        *id)
{
    EBookOp *op;

    printf ("e_book_response_add_contact\n");

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_add_contact: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->id = g_strdup (id);

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}



/**
 * e_book_commit_contact:
 * @book: an #EBook
 * @contact: an #EContact
 *
 * applies the changes made to @contact to the stored version in
 * @book.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_commit_contact (EBook           *book,
               EContact        *contact,
               GError         **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;
    char *vcard_str;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (contact && E_IS_CONTACT (contact), E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_commit_contact called on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling _e_book_response_generic */
    GNOME_Evolution_Addressbook_Book_modifyContact (book->priv->corba_book,
                            (const GNOME_Evolution_Addressbook_VCard) vcard_str, &ev);

    g_free (vcard_str);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::modifyContact call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    e_contact_set (contact, E_CONTACT_UID, our_op->id);
    g_free (our_op->id);

    /* remove the op from the book's hash of operations */
    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}


/**
 * e_book_get_supported_fields:
 * @book: an #EBook
 * @fields: a #GList
 *
 * queries @book for the list of fields it supports.  mostly for use
 * by the contact editor so it knows what fields to sensitize.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_get_supported_fields  (EBook            *book,
                  GList           **fields,
                  GError          **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (fields,                   E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_supported_fields on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling
       _e_book_response_get_supported_fields */
    GNOME_Evolution_Addressbook_Book_getSupportedFields(book->priv->corba_book, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getSupportedFields call"));
        return FALSE;
    }


    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *fields = our_op->list;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_supported_fields (EBook       *book,
                      EBookStatus  status,
                      GList       *fields)
{
    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_supported_fields: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->list = fields;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}


/**
 * e_book_get_supported_auth_methods:
 * @book: an #EBook
 * @auth_methods: a #GList
 *
 * queries @book for the list of authentication methods it supports.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_get_supported_auth_methods (EBook            *book,
                   GList           **auth_methods,
                   GError          **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (auth_methods,             E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_supported_auth_methods on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling
       e_book_response_get_supported_fields */
    GNOME_Evolution_Addressbook_Book_getSupportedAuthMethods(book->priv->corba_book, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getSupportedAuthMethods call"));
        return FALSE;
    }


    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *auth_methods = our_op->list;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_supported_auth_methods (EBook                 *book,
                        EBookStatus            status,
                        GList                 *auth_methods)
{
    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_supported_auth_methods: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->list = auth_methods;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}



/**
 * e_book_authenticate_user:
 * @book: an #EBook
 * @user: a string
 * @passwd: a string
 * @auth_method: a string
 *
 * authenticates @user with @passwd, using the auth method
 * @auth_method.  @auth_method must be one of the authentication
 * methods returned using e_book_get_supported_auth_methods.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_authenticate_user (EBook         *book,
              const char    *user,
              const char    *passwd,
              const char    *auth_method,
              GError       **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (user,                     E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (passwd,                   E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (auth_method,              E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_authenticate_user on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling
       e_book_response_generic */
    GNOME_Evolution_Addressbook_Book_authenticateUser (book->priv->corba_book,
                               user, passwd,
                               auth_method,
                               &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::authenticateUser call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}


/**
 * e_book_get_contact:
 * @book: an #EBook
 * @id: a string
 * @contact: an #EContact
 *
 * Fills in @contact with the contents of the vcard in @book
 * corresponding to @id.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_get_contact (EBook       *book,
            const char  *id,
            EContact   **contact,
            GError     **error)
{
    EBookOp *our_op;
    EBookStatus status;
    CORBA_Environment ev;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (id,                       E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (contact,                     E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_contact on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling e_book_response_generic */
    GNOME_Evolution_Addressbook_Book_getContact (book->priv->corba_book,
                             (const GNOME_Evolution_Addressbook_VCard) id, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getContact call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *contact = our_op->contact;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_contact (EBook       *book,
                 EBookStatus  status,
                 EContact    *contact)
{
    EBookOp *op;

    printf ("e_book_response_get_contact\n");

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_contact: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->contact = contact;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}


/**
 * e_book_remove_contact:
 * @book: an #EBook
 * @id: a string
 *
 * Removes the contact with id @id from @book.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_remove_contact (EBook       *book,
               const char  *id,
               GError     **error)
{
    GList *list;
    gboolean rv;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (id,                       E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_remove_contact on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    e_mutex_lock (book->priv->mutex);

    list = g_list_append (NULL, (char*)id);

    rv = e_book_remove_contacts (book, list, error);

    return rv;
}

/**
 * e_book_remove_contacts:
 * @book: an #EBook
 * @ids: an #GList of const char *id's
 *
 * Removes the contacts with ids from the list @ids from @book.  This is
 * always more efficient than calling e_book_remove_contact_by_id if you
 * have more than one id to remove, as some backends can implement it
 * as a batch request.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_remove_contacts (EBook    *book,
            GList    *ids,
            GError  **error)
{
    GNOME_Evolution_Addressbook_ContactIdList idlist;
    CORBA_Environment ev;
    GList *iter;
    int num_ids, i;
    EBookOp *our_op;
    EBookStatus status;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (ids,                      E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_remove_contacts on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    num_ids = g_list_length (ids);
    idlist._buffer = CORBA_sequence_GNOME_Evolution_Addressbook_ContactId_allocbuf (num_ids);
    idlist._maximum = num_ids;
    idlist._length = num_ids;

    for (iter = ids, i = 0; iter; iter = iter->next)
        idlist._buffer[i++] = CORBA_string_dup (iter->data);

    /* will eventually end up calling e_book_response_generic */
    GNOME_Evolution_Addressbook_Book_removeContacts (book->priv->corba_book, &idlist, &ev);

    CORBA_free(idlist._buffer);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::removeContacts call"));
        return FALSE;
    }
    
    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}


/**
 * e_book_get_book_view:
 * @book: an #EBook
 * @query: an #EBookQuery
 * @requested_fields a #GList containing the names of fields to return, or NULL for all
 * @max_results the maximum number of contacts to show (or 0 for all)
 *
 * need docs here..
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_get_book_view (EBook       *book,
              EBookQuery  *query,
              GList       *requested_fields,
              int          max_results,
              EBookView  **book_view,
              GError     **error)
{
    GNOME_Evolution_Addressbook_stringlist stringlist;
    CORBA_Environment ev;
    EBookOp *our_op;
    EBookStatus status;
    int num_fields, i;
    GList *iter;
    char *query_string;

    e_return_error_if_fail (book && E_IS_BOOK (book),       E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (query,                          E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (book_view,                      E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_book_view on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    our_op->listener = e_book_view_listener_new();

    num_fields = g_list_length (requested_fields);

    stringlist._buffer = CORBA_sequence_CORBA_string_allocbuf (num_fields);
    stringlist._maximum = num_fields;
    stringlist._length = num_fields;

    for (i = 0, iter = requested_fields; iter; iter = iter->next, i ++) {
        stringlist._buffer[i] = CORBA_string_dup ((char*)iter->data);
    }

    query_string = e_book_query_to_string (query);

    /* will eventually end up calling e_book_response_get_book_view */
    GNOME_Evolution_Addressbook_Book_getBookView (book->priv->corba_book,
                              bonobo_object_corba_objref(BONOBO_OBJECT(our_op->listener)),
                              query_string,
                              &stringlist, max_results, &ev);

    CORBA_free(stringlist._buffer);
    g_free (query_string);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_warning ("corba exception._major = %d\n", ev._major);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getBookView call"));
        return FALSE;
    }
    
    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *book_view = our_op->view;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_book_view (EBook       *book,
                   EBookStatus  status,
                   GNOME_Evolution_Addressbook_BookView corba_book_view)
{

    EBookOp *op;

    printf ("e_book_response_get_book_view\n");

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_book_view: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->view = e_book_view_new (corba_book_view, op->listener);

    bonobo_object_ref(BONOBO_OBJECT(op->listener));

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}



/**
 * e_book_get_contacts:
 * @book: an #EBook
 * @query: an #EBookQuery
 *
 * need docs here..
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_get_contacts (EBook       *book,
             EBookQuery  *query,
             GList      **contacts,
             GError     **error)
{
    CORBA_Environment ev;
    EBookOp *our_op;
    EBookStatus status;
    char *query_string;

    e_return_error_if_fail (book && E_IS_BOOK (book),       E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (query,                          E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (contacts,                       E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_contacts on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    query_string = e_book_query_to_string (query);

    /* will eventually end up calling e_book_response_get_contacts */
    GNOME_Evolution_Addressbook_Book_getContactList (book->priv->corba_book, query_string, &ev);

    g_free (query_string);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_warning ("corba exception._major = %d\n", ev._major);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getContactList call"));
        return FALSE;
    }
    
    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *contacts = our_op->list;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_contacts (EBook       *book,
                  EBookStatus  status,
                  GList       *contact_list)
{

    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_contacts: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->list = contact_list;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}


gboolean
e_book_get_changes (EBook       *book,
            char        *changeid,
            GList      **changes,
            GError     **error)
{
    CORBA_Environment ev;
    EBookOp *our_op;
    EBookStatus status;

    e_return_error_if_fail (book && E_IS_BOOK (book),       E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (changeid,                       E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (changes,                        E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_get_changes on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling e_book_response_get_changes */
    GNOME_Evolution_Addressbook_Book_getChanges (book->priv->corba_book, changeid, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_warning ("corba exception._major = %d\n", ev._major);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::getChanges call"));
        return FALSE;
    }
    
    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;
    *changes = our_op->list;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_get_changes (EBook       *book,
                 EBookStatus  status,
                 GList       *change_list)
{

    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_get_contacts: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;
    op->list = change_list;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}

void
e_book_free_change_list (GList *change_list)
{
    GList *l;
    for (l = change_list; l; l = l->next) {
        EBookChange *change = l->data;

        g_object_unref (change->contact);
        g_free (change);
    }

    g_list_free (change_list);
}



static void
e_book_response_generic (EBook       *book,
             EBookStatus  status)
{
    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_generic: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}

/**
 * e_book_cancel:
 * @book: an #EBook
 *
 * Used to cancel an already running operation on @book.  This
 * function makes a synchronous CORBA to the backend telling it to
 * cancel the operation.  If the operation wasn't cancellable (either
 * transiently or permanently) or had already comopleted on the wombat
 * side, this function will return E_BOOK_STATUS_COULD_NOT_CANCEL, and
 * the operation will continue uncancelled.  If the operation could be
 * cancelled, this function will return E_BOOK_ERROR_OK, and the
 * blocked e_book function corresponding to current operation will
 * return with a status of E_BOOK_STATUS_CANCELLED.
 *
 * Return value: a #EBookStatus value.
 **/
gboolean
e_book_cancel (EBook   *book,
           GError **error)
{
    EBookOp *op;
    EBookStatus status;
    gboolean rv;
    CORBA_Environment ev;

    e_mutex_lock (book->priv->mutex);

    if (book->priv->current_op == NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_COULD_NOT_CANCEL,
                 _("e_book_cancel: there is no current operation"));
        return FALSE;
    }

    op = book->priv->current_op;

    e_mutex_lock (op->mutex);

    e_mutex_unlock (book->priv->mutex);

    status = GNOME_Evolution_Addressbook_Book_cancelOperation(book->priv->corba_book, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_mutex_unlock (op->mutex);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::cancelOperation call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    if (status == E_BOOK_ERROR_OK) {
        op->status = E_BOOK_ERROR_CANCELLED;

        pthread_cond_signal (&op->cond);

        rv = TRUE;
    }
    else {
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_COULD_NOT_CANCEL,
                 _("e_book_cancel: couldn't cancel"));
        rv = FALSE;
    }

    e_mutex_unlock (op->mutex);

    return rv;
}

static void
e_book_response_open (EBook       *book,
              EBookStatus  status)
{
    EBookOp *op;

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_open: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}



gboolean
e_book_remove (EBook   *book,
           GError **error)
{
    CORBA_Environment ev;
    EBookOp *our_op;
    EBookStatus status;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);

    e_mutex_lock (book->priv->mutex);

    if (book->priv->load_state != E_BOOK_URI_LOADED) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_URI_NOT_LOADED,
                 _("e_book_remove on book before e_book_load_uri"));
        return FALSE;
    }

    if (book->priv->current_op != NULL) {
        e_mutex_unlock (book->priv->mutex);
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_BUSY,
                 _("book busy"));
        return FALSE;
    }

    our_op = e_book_new_op (book);

    e_mutex_lock (our_op->mutex);

    e_mutex_unlock (book->priv->mutex);

    CORBA_exception_init (&ev);

    /* will eventually end up calling e_book_response_remove */
    GNOME_Evolution_Addressbook_Book_remove (book->priv->corba_book, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {

        e_book_clear_op (book, our_op);

        CORBA_exception_free (&ev);

        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION,
                 _("Corba exception making Book::remove call"));
        return FALSE;
    }

    CORBA_exception_free (&ev);

    /* wait for something to happen (both cancellation and a
       successful response will notity us via our cv */
    e_mutex_cond_wait (&our_op->cond, our_op->mutex);

    status = our_op->status;

    e_book_clear_op (book, our_op);

    E_BOOK_CHECK_STATUS (status, error);
}

static void
e_book_response_remove (EBook       *book,
            EBookStatus  status)
{
    EBookOp *op;

    printf ("e_book_response_remove\n");

    op = e_book_get_op (book);

    if (op == NULL) {
      g_warning ("e_book_response_remove: Cannot find operation ");
      return;
    }

    e_mutex_lock (op->mutex);

    op->status = status;

    pthread_cond_signal (&op->cond);

    e_mutex_unlock (op->mutex);
}



static void
e_book_handle_response (EBookListener *listener, EBookListenerResponse *resp, EBook *book)
{
    switch (resp->op) {
    case CreateContactResponse:
        e_book_response_add_contact (book, resp->status, resp->id);
        break;
    case RemoveContactResponse:
    case ModifyContactResponse:
    case AuthenticationResponse:
        e_book_response_generic (book, resp->status);
        break;
    case GetContactResponse: {
        EContact *contact = e_contact_new_from_vcard (resp->vcard);
        e_book_response_get_contact (book, resp->status, contact);
        break;
    }
    case GetContactListResponse:
        e_book_response_get_contacts (book, resp->status, resp->list);
        break;
    case GetBookViewResponse:
        e_book_response_get_book_view(book, resp->status, resp->book_view);
        break;
    case GetChangesResponse:
        e_book_response_get_changes(book, resp->status, resp->list);
        break;
    case OpenBookResponse:
        e_book_response_open (book, resp->status);
        break;
    case RemoveBookResponse:
        e_book_response_remove (book, resp->status);
        break;
    case GetSupportedFieldsResponse:
        e_book_response_get_supported_fields (book, resp->status, resp->list);
        break;
    case GetSupportedAuthMethodsResponse:
        e_book_response_get_supported_auth_methods (book, resp->status, resp->list);
        break;
    case WritableStatusEvent:
        book->priv->writable = resp->writable;
        g_signal_emit (book, e_book_signals [WRITABLE_STATUS], 0, resp->writable);
        break;
    default:
        g_error ("EBook: Unknown response code %d!\n",
             resp->op);
    }
}



gboolean
e_book_unload_uri (EBook   *book,
           GError **error)
{
    CORBA_Environment ev;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (book->priv->load_state != E_BOOK_URI_NOT_LOADED, E_BOOK_ERROR_URI_NOT_LOADED);

    /* Release the remote GNOME_Evolution_Addressbook_Book in the PAS. */
    CORBA_exception_init (&ev);

    bonobo_object_release_unref  (book->priv->corba_book, &ev);
    if (ev._major != CORBA_NO_EXCEPTION) {
        g_warning ("e_book_unload_uri: Exception releasing "
               "remote book interface!\n");
    }

    CORBA_exception_free (&ev);

    e_book_listener_stop (book->priv->listener);
    bonobo_object_unref (BONOBO_OBJECT (book->priv->listener));

    book->priv->listener   = NULL;
    book->priv->load_state = E_BOOK_URI_NOT_LOADED;
    g_free (book->priv->cap);
    book->priv->cap = NULL;
    book->priv->writable = FALSE;

    return TRUE;
}



/**
 * e_book_load_uri:
 */

static void
backend_died_cb (EComponentListener *cl, gpointer user_data)
{
    EBook *book = user_data;
                                                                                                                              
    book->priv->load_state = E_BOOK_URI_NOT_LOADED;
        g_signal_emit (book, e_book_signals [BACKEND_DIED], 0);
}

static GList *
activate_factories_for_uri (EBook *book, const char *uri)
{
    CORBA_Environment ev;
    Bonobo_ServerInfoList *info_list = NULL;
    int i;
    char *protocol, *query, *colon;
    GList *factories = NULL;

    colon = strchr (uri, ':');
    if (!colon) {
        g_warning ("e_book_load_uri: Unable to determine protocol in the URI\n");
        return FALSE;
    }

    protocol = g_strndup (uri, colon-uri);
    query = g_strdup_printf ("repo_ids.has ('IDL:GNOME/Evolution/BookFactory:1.0')"
                 " AND addressbook:supported_protocols.has ('%s')", protocol
                 );

    CORBA_exception_init (&ev);
    
    info_list = bonobo_activation_query (query, NULL, &ev);

    if (ev._major != CORBA_NO_EXCEPTION) {
        g_warning ("Eeek!  Cannot perform bonobo-activation query for book factories.");
        CORBA_exception_free (&ev);
        goto done;
        return NULL;
    }

    if (info_list->_length == 0) {
        g_warning ("Can't find installed BookFactory that handles protocol '%s'.", protocol);
        CORBA_exception_free (&ev);
        goto done;
    }

    CORBA_exception_free (&ev);

    for (i = 0; i < info_list->_length; i ++) {
        const Bonobo_ServerInfo *info;
        GNOME_Evolution_Addressbook_BookFactory factory;

        info = info_list->_buffer + i;

        factory = bonobo_activation_activate_from_id (info->iid, 0, NULL, NULL);

        if (factory == CORBA_OBJECT_NIL)
            g_warning ("e_book_construct: Could not obtain a handle "
                   "to the Personal Addressbook Server with IID `%s'\n", info->iid);
        else
            factories = g_list_append (factories, factory);
    }

 done:
    if (info_list)
        CORBA_free (info_list);
    g_free (query);
    g_free (protocol);

    return factories;
}

gboolean
e_book_load_uri (EBook        *book,
         const char   *uri,
         gboolean      only_if_exists,
         GError      **error)
{
    GList *factories;
    GList *l;
    gboolean rv = FALSE;
    GNOME_Evolution_Addressbook_Book corba_book = CORBA_OBJECT_NIL;

    e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG);
    e_return_error_if_fail (uri,                      E_BOOK_ERROR_INVALID_ARG);

    /* XXX this needs to happen while holding the book's lock i would think... */
    e_return_error_if_fail (book->priv->load_state == E_BOOK_URI_NOT_LOADED, E_BOOK_ERROR_URI_ALREADY_LOADED);

    /* try to find a list of factories that can handle the protocol */
    if (! (factories = activate_factories_for_uri (book, uri))) {
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED,
                 _("e_book_load_uri: no factories available for uri `%s'"), uri);
        return FALSE;
    }


    book->priv->load_state = E_BOOK_URI_LOADING;

    /*
     * Create our local BookListener interface.
     */
    book->priv->listener = e_book_listener_new ();
    if (book->priv->listener == NULL) {
        g_warning ("e_book_load_uri: Could not create EBookListener!\n");
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_OTHER_ERROR,
                 _("e_book_load_uri: Could not create EBookListener"));
        return FALSE;
    }
    book->priv->listener_signal = g_signal_connect (book->priv->listener, "response",
                            G_CALLBACK (e_book_handle_response), book);

    g_free (book->priv->uri);
    book->priv->uri = g_strdup (uri);

    for (l = factories; l; l = l->next) {
        GNOME_Evolution_Addressbook_BookFactory factory = l->data;
        EBookOp *our_op;
        CORBA_Environment ev;
        EBookStatus status;

        our_op = e_book_new_op (book);

        e_mutex_lock (our_op->mutex);

        CORBA_exception_init (&ev);

        corba_book = GNOME_Evolution_Addressbook_BookFactory_getBook (factory, book->priv->uri,
                                      bonobo_object_corba_objref (BONOBO_OBJECT (book->priv->listener)),
                                      &ev);

        if (ev._major != CORBA_NO_EXCEPTION) {
            e_book_clear_op (book, our_op);

            CORBA_exception_free (&ev);
            continue;
        }

        GNOME_Evolution_Addressbook_Book_open (corba_book,
                               only_if_exists,
                               &ev);

        if (ev._major != CORBA_NO_EXCEPTION) {
            /* kill the listener so the book will die */
            g_signal_handler_disconnect (book->priv->listener, book->priv->listener_signal);
            bonobo_object_unref (book->priv->listener);
            book->priv->listener = NULL;

            e_book_clear_op (book, our_op);

            CORBA_exception_free (&ev);
            continue;
        }

        CORBA_exception_free (&ev);

        /* wait for something to happen (both cancellation and a
           successful response will notity us via our cv */
        e_mutex_cond_wait (&our_op->cond, our_op->mutex);

        status = our_op->status;

        /* remove the op from the book's hash of operations */
        e_book_clear_op (book, our_op);

        if (status == E_BOOK_ERROR_CANCELLED
            || status == E_BOOK_ERROR_OK) {
            rv = TRUE;
            break;
        }
    }

    /* free up the factories */
    for (l = factories; l; l = l->next)
        CORBA_Object_release ((CORBA_Object)l->data, NULL);

    if (rv == TRUE) {
        book->priv->corba_book = corba_book;
        book->priv->load_state = E_BOOK_URI_LOADED;
        book->priv->comp_listener = e_component_listener_new (book->priv->corba_book);
        book->priv->died_signal = g_signal_connect (book->priv->comp_listener, "component_died",
                                G_CALLBACK (backend_died_cb), book);
        return TRUE;
    }
    else {
        g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED,
                 _("e_book_load_uri: no factories available for uri `%s'"), uri);
        return FALSE;
    }
        
    return rv;
}

gboolean
e_book_load_local_addressbook (EBook   *book,
                   GError **error)
{
    char *filename;
    char *uri;
    gboolean rv;

    filename = g_build_filename (g_get_home_dir(),
                     "evolution/local/Contacts",
                     NULL);
    uri = g_strdup_printf ("file://%s", filename);

    g_free (filename);
    
    rv = e_book_load_uri (book, uri, TRUE, error);
    
    g_free (uri);

    return rv;
}

const char *
e_book_get_uri (EBook *book)
{
    return book->priv->uri;
}

const char *
e_book_get_static_capabilities (EBook   *book,
                GError **error)
{
    if (!book->priv->cap_queried) {
        CORBA_Environment ev;
        char *temp;

        CORBA_exception_init (&ev);

        if (book->priv->load_state != E_BOOK_URI_LOADED) {
            g_warning ("e_book_unload_uri: No URI is loaded!\n");
            return g_strdup("");
        }

        temp = GNOME_Evolution_Addressbook_Book_getStaticCapabilities(book->priv->corba_book, &ev);

        if (ev._major != CORBA_NO_EXCEPTION) {
            g_warning ("e_book_get_static_capabilities: Exception "
                   "during get_static_capabilities!\n");
            CORBA_exception_free (&ev);
            return g_strdup("");
        }

        book->priv->cap = g_strdup(temp);
        book->priv->cap_queried = TRUE;

        CORBA_free(temp);

        CORBA_exception_free (&ev);
    }

    return book->priv->cap;
}

gboolean
e_book_check_static_capability (EBook *book,
                const char  *cap)
{
    const char *caps = e_book_get_static_capabilities (book, NULL);

    /* XXX this is an inexact test but it works for our use */
    if (caps && strstr (caps, cap))
        return TRUE;

    return FALSE;
}

gboolean
e_book_is_writable (EBook *book)
{
    return book->priv->writable;
}




gboolean
e_book_get_self (EContact **contact, EBook **book, GError **error)
{
    GError *e = NULL;

    if (!e_book_get_default_addressbook (book, &e)) {
        g_propagate_error (error, e);
        return FALSE;
    }

#if notyet
    EBook *b;
    char *self_uri, *self_uid;

    /* XXX get the setting for the self book and self uid from gconf */

    b = e_book_new();
    if (! e_book_load_uri (b, self_uri, TRUE, error)) {
        g_object_unref (b);
        return FALSE;
    }

    if (! e_book_get_contact (b, self_uid,
                  contact, error)) {
        g_object_unref (b);
        return FALSE;
    }

    if (book)
        *book = b;
    else
        g_object_unref (b);
    return TRUE;
#endif
}

gboolean
e_book_set_self (EBook *book, const char *id, GError **error)
{
}



gboolean
e_book_get_default_addressbook (EBook **book, GError **error)
{
    /* XXX for now just load the local ~/evolution/local/Contacts */
    char *path, *uri;
    gboolean rv;

    *book = e_book_new ();

    path = g_build_filename (g_get_home_dir (),
                 "evolution/local/Contacts",
                 NULL);
    uri = g_strdup_printf ("file://%s", path);
    g_free (path);

    rv = e_book_load_uri (*book, uri, FALSE, error);

    g_free (uri);

    if (!rv) {
        g_object_unref (*book);
        *book = NULL;
    }

    return rv;
#if notyet
    EConfigListener *listener = e_config_listener_new ();
    ESourceList *sources = ...;
    ESource *default_source;

    default_source = e_source_list_peek_source_by_uid (sources,
                               "default_");
#endif
}

#if notyet
ESourceList*
e_book_get_addressbooks (GError **error)
{
}
#endif


static void*
startup_mainloop (void *arg)
{
    bonobo_main();
    return NULL;
}

/* one-time start up for libebook */
static void
e_book_activate()
{
    static GStaticMutex e_book_lock = G_STATIC_MUTEX_INIT;
    static gboolean activated = FALSE;

    g_static_mutex_lock (&e_book_lock);
    if (!activated) {
        pthread_t ebook_mainloop_thread;
        activated = TRUE;
        pthread_create(&ebook_mainloop_thread, NULL, startup_mainloop, NULL);
    }
    g_static_mutex_unlock (&e_book_lock);
}



EBook*
e_book_new (void)
{
    e_book_activate ();
    return g_object_new (E_TYPE_BOOK, NULL);
}


static void
e_book_init (EBook *book)
{
    book->priv             = g_new0 (EBookPrivate, 1);
    book->priv->load_state = E_BOOK_URI_NOT_LOADED;
    book->priv->uri        = NULL;
    book->priv->mutex      = e_mutex_new (E_MUTEX_REC);
}

static void
e_book_dispose (GObject *object)
{
    EBook             *book = E_BOOK (object);

    if (book->priv) {
        CORBA_Environment  ev;
        GList *l;

        if (book->priv->comp_listener) {
            g_signal_handler_disconnect (book->priv->comp_listener, book->priv->died_signal);
            g_object_unref (book->priv->comp_listener);
            book->priv->comp_listener = NULL;
        }

        if (book->priv->load_state == E_BOOK_URI_LOADED)
            e_book_unload_uri (book, NULL);

        CORBA_exception_init (&ev);

        for (l = book->priv->book_factories; l; l = l->next) {
            CORBA_Object_release ((CORBA_Object)l->data, &ev);
            if (ev._major != CORBA_NO_EXCEPTION) {
                g_warning ("EBook: Exception while releasing BookFactory\n");

                CORBA_exception_free (&ev);
                CORBA_exception_init (&ev);
            }
        }
        
        CORBA_exception_free (&ev);

        if (book->priv->listener) {
            g_signal_handler_disconnect (book->priv->listener, book->priv->listener_signal);
            bonobo_object_unref (book->priv->listener);
            book->priv->listener = NULL;
        }
        
        g_free (book->priv->cap);

        g_free (book->priv->uri);

        g_free (book->priv);
        book->priv = NULL;
    }

    if (G_OBJECT_CLASS (parent_class)->dispose)
        G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
e_book_class_init (EBookClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    parent_class = g_type_class_ref (G_TYPE_OBJECT);

    e_book_signals [WRITABLE_STATUS] =
        g_signal_new ("writable_status",
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (EBookClass, writable_status),
                  NULL, NULL,
                  e_book_marshal_NONE__BOOL,
                  G_TYPE_NONE, 1,
                  G_TYPE_BOOLEAN);

    e_book_signals [BACKEND_DIED] =
        g_signal_new ("backend_died",
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (EBookClass, backend_died),
                  NULL, NULL,
                  e_book_marshal_NONE__NONE,
                  G_TYPE_NONE, 0);

    object_class->dispose = e_book_dispose;
}

/**
 * e_book_get_type:
 */
GType
e_book_get_type (void)
{
    static GType type = 0;

    if (! type) {
        GTypeInfo info = {
            sizeof (EBookClass),
            NULL, /* base_class_init */
            NULL, /* base_class_finalize */
            (GClassInitFunc)  e_book_class_init,
            NULL, /* class_finalize */
            NULL, /* class_data */
            sizeof (EBook),
            0,    /* n_preallocs */
            (GInstanceInitFunc) e_book_init
        };

        type = g_type_register_static (G_TYPE_OBJECT, "EBook", &info, 0);
    }

    return type;
}