diff options
Diffstat (limited to 'addressbook/backend/ebook/e-book.c')
-rw-r--r-- | addressbook/backend/ebook/e-book.c | 2549 |
1 files changed, 1454 insertions, 1095 deletions
diff --git a/addressbook/backend/ebook/e-book.c b/addressbook/backend/ebook/e-book.c index 08a0aac995..3883cd4b15 100644 --- a/addressbook/backend/ebook/e-book.c +++ b/addressbook/backend/ebook/e-book.c @@ -1,34 +1,86 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * The Evolution addressbook client object. - * - * Author: - * Nat Friedman (nat@ximian.com) - * - * Copyright 1999, 2000, Ximian, Inc. - */ #include <config.h> -#include <glib.h> -#include <glib-object.h> + +#include <pthread.h> + #include <string.h> -#include <bonobo-activation/bonobo-activation.h> -#include "addressbook.h" -#include "e-card-cursor.h" -#include "e-book-listener.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 { - URINotLoaded, - URILoading, - URILoaded + E_BOOK_URI_NOT_LOADED, + E_BOOK_URI_LOADING, + E_BOOK_URI_LOADED } EBookLoadState; struct _EBookPrivate { @@ -39,21 +91,20 @@ struct _EBookPrivate { char *cap; gboolean cap_queried; - EBookListener *listener; + /* cached writable status */ + gboolean writable; + + EBookListener *listener; EComponentListener *comp_listener; GNOME_Evolution_Addressbook_Book corba_book; EBookLoadState load_state; - /* - * The operation queue. New operations are appended to the - * end of the queue. When responses come back from the PAS, - * the op structures are popped off the front of the queue. - */ - GList *pending_ops; - guint op_tag; + EBookOp *current_op; + + EMutex *mutex; gchar *uri; @@ -61,1526 +112,1844 @@ struct _EBookPrivate { gulong died_signal; }; -enum { - OPEN_PROGRESS, - WRITABLE_STATUS, - LINK_STATUS, - BACKEND_DIED, - LAST_SIGNAL -}; - -static guint e_book_signals [LAST_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"); -typedef struct { - guint tag; - gboolean active; - gpointer cb; - gpointer closure; - EBookViewListener *listener; -} EBookOp; + return q; +} -/* - * Local response queue management. - */ + -static void -e_book_op_free (EBookOp *op) -{ - if (op->listener) { - bonobo_object_unref (BONOBO_OBJECT (op->listener)); - op->listener = NULL; - } - g_free (op); -} +/* EBookOp calls */ -static guint -e_book_queue_op (EBook *book, - gpointer cb, - gpointer closure, - EBookViewListener *listener) +static EBookOp* +e_book_new_op (EBook *book) { - EBookOp *op; + EBookOp *op = g_new0 (EBookOp, 1); - op = g_new0 (EBookOp, 1); - op->tag = book->priv->op_tag++; - op->active = TRUE; - op->cb = cb; - op->closure = closure; - op->listener = listener; + op->mutex = e_mutex_new (E_MUTEX_SIMPLE); + pthread_cond_init (&op->cond, 0); - if (op->listener) - bonobo_object_ref (BONOBO_OBJECT (op->listener)); + book->priv->current_op = op; - book->priv->pending_ops = - g_list_append (book->priv->pending_ops, op); + return op; +} - return op->tag; +static EBookOp* +e_book_get_op (EBook *book) +{ + if (!book->priv->current_op) { + g_warning ("unexpected response"); + return NULL; + } + + return book->priv->current_op; } -/* - * Local response queue management. - */ static void -e_book_unqueue_op (EBook *book) +e_book_op_free (EBookOp *op) { - EBookOp *op; - GList *removed; + /* XXX more stuff here */ + pthread_cond_destroy (&op->cond); + e_mutex_destroy (op->mutex); + g_free (op); +} - removed = g_list_last (book->priv->pending_ops); +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"); - if (removed) { - book->priv->pending_ops = g_list_remove_link (book->priv->pending_ops, - removed); - op = removed->data; - e_book_op_free (op); - g_list_free_1 (removed); - book->priv->op_tag--; - } + book->priv->current_op = NULL; } -static EBookOp * -e_book_pop_op (EBook *book) +static void +e_book_clear_op (EBook *book, + EBookOp *op) { - GList *popped; - EBookOp *op; + e_book_op_remove (book, op); + e_mutex_unlock (op->mutex); + e_book_op_free (op); +} - if (book->priv->pending_ops == NULL) - return NULL; + - op = book->priv->pending_ops->data; +/** + * 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; - popped = book->priv->pending_ops; - book->priv->pending_ops = - g_list_remove_link (book->priv->pending_ops, - book->priv->pending_ops); + printf ("e_book_add_contact\n"); - g_list_free_1 (popped); + 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); - return op; -} + e_mutex_lock (book->priv->mutex); -static gboolean -e_book_cancel_op (EBook *book, guint tag) -{ - GList *iter; - gboolean cancelled = FALSE; + 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; + } - for (iter = book->priv->pending_ops; iter != NULL && !cancelled; iter = g_list_next (iter)) { - EBookOp *op = iter->data; - if (op->tag == tag) { - op->active = FALSE; - cancelled = TRUE; - } + 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; } - - return cancelled; -} -static void -e_book_do_response_create_card (EBook *book, - EBookListenerResponse *resp) -{ - EBookOp *op; + vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - op = e_book_pop_op (book); + our_op = e_book_new_op (book); - if (op == NULL) { - g_warning ("e_book_do_response_create_card: Cannot find operation " - "in local op queue!\n"); - return; - } + e_mutex_lock (our_op->mutex); - if (op->cb) - ((EBookIdCallback) op->cb) (book, resp->status, resp->id, op->closure); - g_free (resp->id); - e_book_op_free (op); -} + e_mutex_unlock (book->priv->mutex); -static void -e_book_do_response_generic (EBook *book, - EBookListenerResponse *resp) -{ - EBookOp *op; + CORBA_exception_init (&ev); - op = e_book_pop_op (book); + /* 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); - if (op == NULL) { - g_warning ("e_book_do_response_generic: Cannot find operation " - "in local op queue!\n"); + 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; } - if (op->cb) - ((EBookCallback) op->cb) (book, resp->status, op->closure); + CORBA_exception_free (&ev); - e_book_op_free (op); + /* 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_do_response_get_vcard (EBook *book, - EBookListenerResponse *resp) +e_book_response_add_contact (EBook *book, + EBookStatus status, + char *id) { EBookOp *op; - ECard *card; - op = e_book_pop_op (book); + printf ("e_book_response_add_contact\n"); + + op = e_book_get_op (book); if (op == NULL) { - g_warning ("e_book_do_response_get_vcard: Cannot find operation " - "in local op queue!\n"); - return; + g_warning ("e_book_response_add_contact: Cannot find operation "); + return; } - if (resp->vcard != NULL) { - card = e_card_new(resp->vcard); + e_mutex_lock (op->mutex); - if (card != NULL) { - e_card_set_book (card, book); - if (op->cb) { - if (op->active) - ((EBookCardCallback) op->cb) (book, resp->status, card, op->closure); - else - ((EBookCardCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } + op->status = status; + op->id = g_strdup (id); - g_object_unref(card); - } else { - ((EBookCursorCallback) op->cb) (book, resp->status, NULL, op->closure); - } - } else { - ((EBookCardCallback) op->cb) (book, resp->status, NULL, op->closure); - } + pthread_cond_signal (&op->cond); - g_free (resp->vcard); - e_book_op_free (op); + e_mutex_unlock (op->mutex); } -static void -e_book_do_response_get_cursor (EBook *book, - EBookListenerResponse *resp) + + +/** + * 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; - EBookOp *op; - ECardCursor *cursor; + char *vcard_str; - op = e_book_pop_op (book); + 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); - if (op == NULL) { - g_warning ("e_book_do_response_get_cursor: Cannot find operation " - "in local op queue!\n"); - return; + 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; } - cursor = e_card_cursor_new(resp->cursor); + 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; + } - if (cursor != NULL) { - if (op->cb) { - if (op->active) - ((EBookCursorCallback) op->cb) (book, resp->status, cursor, op->closure); - else - ((EBookCursorCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } + vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - /* - * Release the remote GNOME_Evolution_Addressbook_Book in the PAS. - */ - CORBA_exception_init (&ev); + our_op = e_book_new_op (book); - bonobo_object_release_unref (resp->cursor, &ev); + e_mutex_lock (our_op->mutex); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_do_response_get_cursor: Exception releasing " - "remote GNOME_Evolution_Addressbook_CardCursor interface!\n"); - } + e_mutex_unlock (book->priv->mutex); - CORBA_exception_free (&ev); + CORBA_exception_init (&ev); - g_object_unref(cursor); - } else { - ((EBookCursorCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } - - e_book_op_free (op); -} + /* 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); -static void -e_book_do_response_get_view (EBook *book, - EBookListenerResponse *resp) -{ - CORBA_Environment ev; - EBookOp *op; - EBookView *book_view; + g_free (vcard_str); - op = e_book_pop_op (book); + if (ev._major != CORBA_NO_EXCEPTION) { - if (op == NULL) { - g_warning ("e_book_do_response_get_view: Cannot find operation " - "in local op queue!\n"); - return; + 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; } - - book_view = e_book_view_new (resp->book_view, op->listener); - - if (book_view != NULL) { - e_book_view_set_book (book_view, book); - - /* Only execute the callback if the operation is still flagged as active (i.e. hasn't - been cancelled. This is mildly wasteful since we unnecessaryily create the - book_view, etc... but I'm leery of tinkering with the CORBA magic. */ - if (op->cb) { - if (op->active) - ((EBookBookViewCallback) op->cb) (book, resp->status, book_view, op->closure); - else - ((EBookBookViewCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } - - /* - * Release the remote GNOME_Evolution_Addressbook_Book in the PAS. - */ - CORBA_exception_init (&ev); - bonobo_object_release_unref (resp->book_view, &ev); + CORBA_exception_free (&ev); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_do_response_get_view: Exception releasing " - "remote GNOME_Evolution_Addressbook_BookView interface!\n"); - } + /* 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); - CORBA_exception_free (&ev); + status = our_op->status; + e_contact_set (contact, E_CONTACT_UID, our_op->id); + g_free (our_op->id); - g_object_unref(book_view); - } else { - e_book_view_listener_stop (op->listener); - ((EBookBookViewCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } + /* remove the op from the book's hash of operations */ + e_book_clear_op (book, our_op); - e_book_op_free (op); + E_BOOK_CHECK_STATUS (status, error); } -static void -e_book_do_response_get_changes (EBook *book, - EBookListenerResponse *resp) + +/** + * 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; - EBookOp *op; - EBookView *book_view; - op = e_book_pop_op (book); + 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); - if (op == NULL) { - g_warning ("e_book_do_response_get_changes: Cannot find operation " - "in local op queue!\n"); - return; + 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; } - book_view = e_book_view_new (resp->book_view, op->listener); + 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")); + } - if (book_view != NULL) { - e_book_view_set_book (book_view, book); - - if (op->cb) { - if (op->active) - ((EBookBookViewCallback) op->cb) (book, resp->status, book_view, op->closure); - else - ((EBookBookViewCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); - } + our_op = e_book_new_op (book); - /* - * Release the remote GNOME_Evolution_Addressbook_BookView in the PAS. - */ - CORBA_exception_init (&ev); + e_mutex_lock (our_op->mutex); - bonobo_object_release_unref (resp->book_view, &ev); + e_mutex_unlock (book->priv->mutex); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_do_response_get_changes: Exception releasing " - "remote GNOME_Evolution_Addressbook_BookView interface!\n"); - } + 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_object_unref(book_view); - } else { - e_book_view_listener_stop (op->listener); - ((EBookBookViewCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::getSupportedFields call")); + return FALSE; } - e_book_op_free (op); -} -static void -backend_died_cb (EComponentListener *cl, gpointer user_data) -{ - EBook *book = user_data; + CORBA_exception_free (&ev); - book->priv->load_state = URINotLoaded; - g_signal_emit (book, e_book_signals [BACKEND_DIED], 0); + /* 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_do_response_open (EBook *book, - EBookListenerResponse *resp) +e_book_response_get_supported_fields (EBook *book, + EBookStatus status, + GList *fields) { EBookOp *op; - if (resp->status == E_BOOK_STATUS_SUCCESS) { - book->priv->corba_book = resp->book; - book->priv->load_state = URILoaded; - - 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); - } - - op = e_book_pop_op (book); + op = e_book_get_op (book); if (op == NULL) { - g_warning ("e_book_do_response_open: Cannot find operation " - "in local op queue!\n"); - return; + g_warning ("e_book_response_get_supported_fields: Cannot find operation "); + return; } - if (op->cb) - ((EBookCallback) op->cb) (book, resp->status, op->closure); - e_book_op_free (op); -} + e_mutex_lock (op->mutex); -static void -e_book_do_progress_event (EBook *book, - EBookListenerResponse *resp) -{ - g_signal_emit (book, e_book_signals [OPEN_PROGRESS], 0, - resp->msg, resp->percent); + op->status = status; + op->list = fields; - g_free (resp->msg); -} + pthread_cond_signal (&op->cond); -static void -e_book_do_link_event (EBook *book, - EBookListenerResponse *resp) -{ - g_signal_emit (book, e_book_signals [LINK_STATUS], 0, - resp->connected); + e_mutex_unlock (op->mutex); } -static void -e_book_do_writable_event (EBook *book, - EBookListenerResponse *resp) + +/** + * 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) { - g_signal_emit (book, e_book_signals [WRITABLE_STATUS], 0, - resp->writable); -} + EBookOp *our_op; + EBookStatus status; + CORBA_Environment ev; -static void -e_book_do_response_get_supported_fields (EBook *book, - EBookListenerResponse *resp) -{ - EBookOp *op; + 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); - op = e_book_pop_op (book); + e_mutex_lock (book->priv->mutex); - if (op == NULL) { - g_warning ("e_book_do_response_get_supported_fields: Cannot find operation " - "in local op queue!\n"); - return; + 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 (op->cb) { - if (op->active) - ((EBookFieldsCallback) op->cb) (book, resp->status, resp->list, op->closure); - else - ((EBookFieldsCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); + 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; } - g_object_unref(resp->list); + our_op = e_book_new_op (book); - e_book_op_free (op); -} + e_mutex_lock (our_op->mutex); -static void -e_book_do_response_get_supported_auth_methods (EBook *book, - EBookListenerResponse *resp) -{ - EBookOp *op; + e_mutex_unlock (book->priv->mutex); - op = e_book_pop_op (book); + CORBA_exception_init (&ev); - if (op == NULL) { - g_warning ("e_book_do_response_get_supported_auth_methods: Cannot find operation " - "in local op queue!\n"); - return; - } + /* will eventually end up calling + e_book_response_get_supported_fields */ + GNOME_Evolution_Addressbook_Book_getSupportedAuthMethods(book->priv->corba_book, &ev); - if (op->cb) { - if (op->active) - ((EBookAuthMethodsCallback) op->cb) (book, resp->status, resp->list, op->closure); - else - ((EBookAuthMethodsCallback) op->cb) (book, E_BOOK_STATUS_CANCELLED, NULL, op->closure); + 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; } - g_object_unref(resp->list); - e_book_op_free (op); + 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); } -/* - * Reading notices out of the EBookListener's queue. - */ static void -e_book_check_listener_queue (EBookListener *listener, EBook *book) +e_book_response_get_supported_auth_methods (EBook *book, + EBookStatus status, + GList *auth_methods) { - EBookListenerResponse *resp; + EBookOp *op; - resp = e_book_listener_pop_response (listener); + op = e_book_get_op (book); - if (resp == NULL) - return; + if (op == NULL) { + g_warning ("e_book_response_get_supported_auth_methods: Cannot find operation "); + return; + } - switch (resp->op) { - case CreateCardResponse: - e_book_do_response_create_card (book, resp); - break; - case RemoveCardResponse: - case ModifyCardResponse: - case AuthenticationResponse: - e_book_do_response_generic (book, resp); - break; - case GetCardResponse: - e_book_do_response_get_vcard (book, resp); - break; - case GetCursorResponse: - e_book_do_response_get_cursor (book, resp); - break; - case GetBookViewResponse: - e_book_do_response_get_view(book, resp); - break; - case GetChangesResponse: - e_book_do_response_get_changes(book, resp); - break; - case OpenBookResponse: - e_book_do_response_open (book, resp); - break; - case GetSupportedFieldsResponse: - e_book_do_response_get_supported_fields (book, resp); - break; - case GetSupportedAuthMethodsResponse: - e_book_do_response_get_supported_auth_methods (book, resp); - break; + e_mutex_lock (op->mutex); - case OpenProgressEvent: - e_book_do_progress_event (book, resp); - break; - case LinkStatusEvent: - e_book_do_link_event (book, resp); - break; - case WritableStatusEvent: - e_book_do_writable_event (book, resp); - break; - default: - g_error ("EBook: Unknown operation %d in listener queue!\n", - resp->op); - } + op->status = status; + op->list = auth_methods; - g_free (resp); + pthread_cond_signal (&op->cond); + + e_mutex_unlock (op->mutex); } + + /** - * e_book_load_uri: - */ + * 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; -typedef struct { - char *uri; - EBookCallback open_response; - gpointer closure; -} EBookLoadURIData; + 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); -static void e_book_load_uri_from_factory (EBook *book, - GNOME_Evolution_Addressbook_BookFactory factory, - EBookLoadURIData *load_uri_data); + e_mutex_lock (book->priv->mutex); -static void -e_book_load_uri_step (EBook *book, EBookStatus status, EBookLoadURIData *data) -{ - /* iterate to the next possible CardFactory, or fail - if it's the last one */ - book->priv->iter = book->priv->iter->next; - if (book->priv->iter) { - GNOME_Evolution_Addressbook_BookFactory factory = book->priv->iter->data; - e_book_load_uri_from_factory (book, factory, data); + 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; } - else { - EBookCallback cb = data->open_response; - gpointer closure = data->closure; - - /* reset the load_state to NotLoaded so people can - attempt another load_uri on the book. */ - book->priv->load_state = URINotLoaded; - g_free (data); - - cb (book, status, closure); + 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; } -} - -static void -e_book_load_uri_open_cb (EBook *book, EBookStatus status, EBookLoadURIData *data) -{ - if (status == E_BOOK_STATUS_SUCCESS) { - EBookCallback cb = data->open_response; - gpointer closure = data->closure; - g_free (data); + our_op = e_book_new_op (book); - cb (book, status, closure); - } - else { - e_book_load_uri_step (book, status, data); - } -} + e_mutex_lock (our_op->mutex); -static void -e_book_load_uri_from_factory (EBook *book, - GNOME_Evolution_Addressbook_BookFactory factory, - EBookLoadURIData *load_uri_data) -{ - CORBA_Environment ev; + e_mutex_unlock (book->priv->mutex); CORBA_exception_init (&ev); - e_book_queue_op (book, e_book_load_uri_open_cb, load_uri_data, NULL); - - GNOME_Evolution_Addressbook_BookFactory_openBook ( - factory, book->priv->uri, - bonobo_object_corba_objref (BONOBO_OBJECT (book->priv->listener)), - &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) { - g_warning ("e_book_load_uri: CORBA exception while opening addressbook!\n"); - e_book_unqueue_op (book); + + e_book_clear_op (book, our_op); + CORBA_exception_free (&ev); - e_book_load_uri_step (book, E_BOOK_STATUS_OTHER_ERROR, load_uri_data); + + 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); } + -static gboolean -activate_factories_for_uri (EBook *book, const char *uri) +/** + * 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; - Bonobo_ServerInfoList *info_list = NULL; - int i; - char *protocol, *query, *colon; - gboolean retval = FALSE; - colon = strchr (uri, ':'); - if (!colon) { - g_warning ("e_book_load_uri: Unable to determine protocol in the URI\n"); + 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; } - 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 - ); + 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); - - info_list = bonobo_activation_query (query, NULL, &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) { - g_warning ("Eeek! Cannot perform bonobo-activation query for book factories."); - CORBA_exception_free (&ev); - goto shutdown; - } - if (info_list->_length == 0) { - g_warning ("Can't find installed BookFactory that handles protocol '%s'.", protocol); + e_book_clear_op (book, our_op); + CORBA_exception_free (&ev); - goto shutdown; + + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::getContact call")); + return FALSE; } CORBA_exception_free (&ev); - for (i = 0; i < info_list->_length; i ++) { - const Bonobo_ServerInfo *info; - GNOME_Evolution_Addressbook_BookFactory factory; + /* 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); - info = info_list->_buffer + i; + status = our_op->status; + *contact = our_op->contact; - factory = bonobo_activation_activate_from_id (info->iid, 0, NULL, NULL); + e_book_clear_op (book, our_op); - 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 - book->priv->book_factories = g_list_append (book->priv->book_factories, - factory); - } + E_BOOK_CHECK_STATUS (status, error); +} - if (!book->priv->book_factories) { - g_warning ("Couldn't activate any book factories."); - goto shutdown; +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; } - retval = TRUE; + e_mutex_lock (op->mutex); - shutdown: - if (info_list) - CORBA_free (info_list); - g_free (query); - g_free (protocol); + op->status = status; + op->contact = contact; - return retval; + pthread_cond_signal (&op->cond); + + e_mutex_unlock (op->mutex); } -void -e_book_load_uri (EBook *book, - const char *uri, - EBookCallback open_response, - gpointer closure) + +/** + * 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) { - EBookLoadURIData *load_uri_data; - GNOME_Evolution_Addressbook_BookFactory factory; + GList *list; + gboolean rv; - g_return_if_fail (book != NULL); - g_return_if_fail (E_IS_BOOK (book)); - g_return_if_fail (uri != NULL); - g_return_if_fail (open_response != NULL); + 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); - if (book->priv->load_state != URINotLoaded) { - g_warning ("e_book_load_uri: Attempted to load a URI " - "on a book which already has a URI loaded!\n"); - open_response (book, E_BOOK_STATUS_OTHER_ERROR, closure); /* XXX need a new status code here */ - return; - } + e_mutex_lock (book->priv->mutex); - /* try to find a list of factories that can handle the protocol */ - if (!activate_factories_for_uri (book, uri)) { - open_response (book, E_BOOK_STATUS_PROTOCOL_NOT_SUPPORTED, closure); - return; + 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; } - - g_free (book->priv->uri); - book->priv->uri = g_strdup (uri); - /* - * 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"); - open_response (NULL, E_BOOK_STATUS_OTHER_ERROR, closure); /* XXX need a new status code here */ - return; + 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; } - book->priv->listener_signal = g_signal_connect (book->priv->listener, "responses_queued", - G_CALLBACK (e_book_check_listener_queue), book); + e_mutex_lock (book->priv->mutex); - load_uri_data = g_new (EBookLoadURIData, 1); - load_uri_data->open_response = open_response; - load_uri_data->closure = closure; + list = g_list_append (NULL, (char*)id); - /* initialize the iterator, and load from the first one*/ - book->priv->iter = book->priv->book_factories; + rv = e_book_remove_contacts (book, list, error); - factory = book->priv->iter->data; - - e_book_load_uri_from_factory (book, factory, load_uri_data); - - book->priv->load_state = URILoading; + return rv; } /** - * e_book_unload_uri: - */ -void -e_book_unload_uri (EBook *book) + * 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; - g_return_if_fail (book != NULL); - g_return_if_fail (E_IS_BOOK (book)); + 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); - /* - * FIXME: Make sure this works if the URI is still being - * loaded. - */ - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_unload_uri: No URI is loaded!\n"); - return; - } + e_mutex_lock (book->priv->mutex); - /* - * Release the remote GNOME_Evolution_Addressbook_Book in the PAS. - */ - CORBA_exception_init (&ev); + 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; + } - 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"); + 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; } - CORBA_exception_free (&ev); + our_op = e_book_new_op (book); - e_book_listener_stop (book->priv->listener); - bonobo_object_unref (BONOBO_OBJECT (book->priv->listener)); + e_mutex_lock (our_op->mutex); - book->priv->listener = NULL; - book->priv->load_state = URINotLoaded; -} + e_mutex_unlock (book->priv->mutex); -const char * -e_book_get_uri (EBook *book) -{ - g_return_val_if_fail (book && E_IS_BOOK (book), NULL); - - return book->priv->uri; -} - -char * -e_book_get_static_capabilities (EBook *book) -{ - if (!book->priv->cap_queried) { - CORBA_Environment ev; - char *temp; + CORBA_exception_init (&ev); - 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; - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_unload_uri: No URI is loaded!\n"); - return g_strdup(""); - } + for (iter = ids, i = 0; iter; iter = iter->next) + idlist._buffer[i++] = CORBA_string_dup (iter->data); - temp = GNOME_Evolution_Addressbook_Book_getStaticCapabilities(book->priv->corba_book, &ev); + /* will eventually end up calling e_book_response_generic */ + GNOME_Evolution_Addressbook_Book_removeContacts (book->priv->corba_book, &idlist, &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(""); - } + CORBA_free(idlist._buffer); - book->priv->cap = g_strdup(temp); - book->priv->cap_queried = TRUE; + if (ev._major != CORBA_NO_EXCEPTION) { - CORBA_free(temp); + e_book_clear_op (book, our_op); CORBA_exception_free (&ev); - } - return g_strdup (book->priv->cap); -} - -gboolean -e_book_check_static_capability (EBook *book, const char *cap) -{ - gboolean rv = FALSE; - char *caps = e_book_get_static_capabilities (book); - if (!caps) + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::removeContacts call")); return FALSE; + } + + CORBA_exception_free (&ev); - /* XXX this is an inexact test but it works for our use */ - if (strstr (caps, cap)) - rv = TRUE; + /* 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); - g_free (caps); + status = our_op->status; - return rv; + e_book_clear_op (book, our_op); + + E_BOOK_CHECK_STATUS (status, error); } -guint -e_book_get_supported_fields (EBook *book, - EBookFieldsCallback cb, - gpointer closure) + +/** + * 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; - guint tag; + EBookOp *our_op; + EBookStatus status; + int num_fields, i; + GList *iter; + char *query_string; - CORBA_exception_init (&ev); + 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); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_unload_uri: No URI is loaded!\n"); - return 0; - } + e_mutex_lock (book->priv->mutex); - tag = e_book_queue_op (book, cb, closure, NULL); - - GNOME_Evolution_Addressbook_Book_getSupportedFields(book->priv->corba_book, &ev); + 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 (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_supported_fields: Exception " - "during get_supported_fields!\n"); - CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; + 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; } - CORBA_exception_free (&ev); + our_op = e_book_new_op (book); - return tag; -} + e_mutex_lock (our_op->mutex); -guint -e_book_get_supported_auth_methods (EBook *book, - EBookAuthMethodsCallback cb, - gpointer closure) -{ - CORBA_Environment ev; - guint tag; + e_mutex_unlock (book->priv->mutex); CORBA_exception_init (&ev); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_unload_uri: No URI is loaded!\n"); - return 0; - } + our_op->listener = e_book_view_listener_new(); - tag = e_book_queue_op (book, cb, closure, NULL); + num_fields = g_list_length (requested_fields); - GNOME_Evolution_Addressbook_Book_getSupportedAuthMethods(book->priv->corba_book, &ev); + stringlist._buffer = CORBA_sequence_CORBA_string_allocbuf (num_fields); + stringlist._maximum = num_fields; + stringlist._length = num_fields; - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_supported_auth_methods: Exception " - "during get_supported_auth_methods!\n"); - CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; + for (i = 0, iter = requested_fields; iter; iter = iter->next, i ++) { + stringlist._buffer[i] = CORBA_string_dup ((char*)iter->data); } - CORBA_exception_free (&ev); + query_string = e_book_query_to_string (query); - return tag; -} + /* 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); -static gboolean -e_book_construct (EBook *book) -{ - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); + CORBA_free(stringlist._buffer); + g_free (query_string); - book->priv->book_factories = NULL; + if (ev._major != CORBA_NO_EXCEPTION) { - return TRUE; -} + e_book_clear_op (book, our_op); -/** - * e_book_new: - */ -EBook * -e_book_new (void) -{ - EBook *book; + CORBA_exception_free (&ev); - book = g_object_new (E_TYPE_BOOK, NULL); + g_warning ("corba exception._major = %d\n", ev._major); - if (! e_book_construct (book)) { - g_object_unref (book); - return NULL; + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::getBookView call")); + return FALSE; } + + CORBA_exception_free (&ev); - return book; -} + /* 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); -/* User authentication. */ + status = our_op->status; + *book_view = our_op->view; -void -e_book_authenticate_user (EBook *book, - const char *user, - const char *passwd, - const char *auth_method, - EBookCallback cb, - gpointer closure) -{ - CORBA_Environment ev; + e_book_clear_op (book, our_op); - g_return_if_fail (book != NULL); - g_return_if_fail (E_IS_BOOK (book)); + E_BOOK_CHECK_STATUS (status, error); +} - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_authenticate_user: No URI loaded!\n"); - return; - } +static void +e_book_response_get_book_view (EBook *book, + EBookStatus status, + GNOME_Evolution_Addressbook_BookView corba_book_view) +{ - CORBA_exception_init (&ev); + EBookOp *op; - e_book_queue_op (book, cb, closure, NULL); + printf ("e_book_response_get_book_view\n"); - GNOME_Evolution_Addressbook_Book_authenticateUser (book->priv->corba_book, - user, - passwd, - auth_method, - &ev); + op = e_book_get_op (book); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_authenticate_user: Exception authenticating user with the PAS!\n"); - CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return; + if (op == NULL) { + g_warning ("e_book_response_get_book_view: Cannot find operation "); + return; } - CORBA_exception_free (&ev); + e_mutex_lock (op->mutex); + + op->status = status; + op->view = e_book_view_new (corba_book_view, op->listener); + + pthread_cond_signal (&op->cond); + + e_mutex_unlock (op->mutex); } -/* Fetching cards */ + /** - * e_book_get_card: - */ -guint -e_book_get_card (EBook *book, - const char *id, - EBookCardCallback cb, - gpointer closure) + * 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; - guint tag; + 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); - g_return_val_if_fail (book != NULL, 0); - g_return_val_if_fail (E_IS_BOOK (book), 0); + 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->load_state != URILoaded) { - g_warning ("e_book_get_card: No URI loaded!\n"); - return 0; + 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); - tag = e_book_queue_op (book, cb, closure, NULL); + query_string = e_book_query_to_string (query); - GNOME_Evolution_Addressbook_Book_getVCard (book->priv->corba_book, (const GNOME_Evolution_Addressbook_VCard) id, &ev); + /* 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) { - g_warning ("e_book_get_card: Exception " - "getting card!\n"); + + e_book_clear_op (book, our_op); + CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; - } + 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); - return tag; -} + /* 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); -/* Deleting cards. */ + status = our_op->status; + *contacts = our_op->list; -/** - * e_book_remove_card: - */ -gboolean -e_book_remove_card (EBook *book, - ECard *card, - EBookCallback cb, - gpointer closure) -{ - const char *id; + e_book_clear_op (book, our_op); - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (card != NULL, FALSE); - g_return_val_if_fail (E_IS_CARD (card), FALSE); + E_BOOK_CHECK_STATUS (status, error); +} - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_remove_card: No URI loaded!\n"); - return FALSE; - } +static void +e_book_response_get_contacts (EBook *book, + EBookStatus status, + GList *contact_list) +{ - id = e_card_get_id (card); - g_assert (id != NULL); + EBookOp *op; - return e_book_remove_card_by_id (book, id, cb, closure); -} + op = e_book_get_op (book); -/** - * e_book_remove_card_by_id: - */ -gboolean -e_book_remove_card_by_id (EBook *book, - const char *id, - EBookCallback cb, - gpointer closure) + if (op == NULL) { + g_warning ("e_book_response_get_contacts: Cannot find operation "); + return; + } -{ - GList *list = NULL; - gboolean rv; + e_mutex_lock (op->mutex); - list = g_list_prepend (list, (char*)id); - - rv = e_book_remove_cards (book, list, cb, closure); + op->status = status; + op->list = contact_list; - g_list_free (list); + pthread_cond_signal (&op->cond); - return rv; + e_mutex_unlock (op->mutex); } + gboolean -e_book_remove_cards (EBook *book, - GList *ids, - EBookCallback cb, - gpointer closure) +e_book_get_changes (EBook *book, + char *changeid, + GList **changes, + GError **error) { - GNOME_Evolution_Addressbook_CardIdList idlist; CORBA_Environment ev; - GList *l; - 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 (changeid, E_BOOK_ERROR_INVALID_ARG); + e_return_error_if_fail (changes, E_BOOK_ERROR_INVALID_ARG); - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (ids != NULL, FALSE); + e_mutex_lock (book->priv->mutex); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_remove_card_by_id: No URI loaded!\n"); + 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; } - CORBA_exception_init (&ev); + 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_book_queue_op (book, cb, closure, NULL); + our_op = e_book_new_op (book); - num_ids = g_list_length (ids); - idlist._buffer = CORBA_sequence_GNOME_Evolution_Addressbook_CardId_allocbuf (num_ids); - idlist._maximum = num_ids; - idlist._length = num_ids; + e_mutex_lock (our_op->mutex); - for (l = ids, i = 0; l; l=l->next, i ++) { - idlist._buffer[i] = CORBA_string_dup (l->data); - } + e_mutex_unlock (book->priv->mutex); - GNOME_Evolution_Addressbook_Book_removeCards (book->priv->corba_book, &idlist, &ev); + 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) { - g_warning ("e_book_remove_card_by_id: CORBA exception " - "talking to PAS!\n"); + + e_book_clear_op (book, our_op); + CORBA_exception_free (&ev); - e_book_unqueue_op (book); + + 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); - CORBA_free(idlist._buffer); + /* 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); - return TRUE; + 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; -/* Adding cards. */ + op = e_book_get_op (book); -/** - * e_book_add_card: - */ -gboolean -e_book_add_card (EBook *book, - ECard *card, - EBookIdCallback cb, - gpointer closure) + if (op == NULL) { + g_warning ("e_book_response_get_contacts: Cannot find operation "); + return; + } -{ - char *vcard; - gboolean retval; + e_mutex_lock (op->mutex); - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (card != NULL, FALSE); - g_return_val_if_fail (E_IS_CARD (card), FALSE); + op->status = status; + op->list = change_list; - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_add_card: No URI loaded!\n"); - return FALSE; + 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_free (change->vcard); + g_free (change); } - vcard = e_card_get_vcard_assume_utf8 (card); + g_list_free (change_list); +} + + - if (vcard == NULL) { - g_warning ("e_book_add_card: Cannot convert card to VCard string!\n"); - return FALSE; +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; } - retval = e_book_add_vcard (book, vcard, cb, closure); + e_mutex_lock (op->mutex); - g_free (vcard); + op->status = status; - e_card_set_book (card, book); + pthread_cond_signal (&op->cond); - return retval; + e_mutex_unlock (op->mutex); } /** - * e_book_add_vcard: - */ + * 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_add_vcard (EBook *book, - const char *vcard, - EBookIdCallback cb, - gpointer closure) +e_book_cancel (EBook *book, + GError **error) { + EBookOp *op; + EBookStatus status; + gboolean rv; CORBA_Environment ev; - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (vcard != NULL, FALSE); + e_mutex_lock (book->priv->mutex); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_add_vcard: No URI loaded!\n"); + 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; } - CORBA_exception_init (&ev); + op = book->priv->current_op; + + e_mutex_lock (op->mutex); - e_book_queue_op (book, (EBookCallback) cb, closure, NULL); + e_mutex_unlock (book->priv->mutex); - GNOME_Evolution_Addressbook_Book_addCard ( - book->priv->corba_book, (const GNOME_Evolution_Addressbook_VCard) vcard, &ev); + status = GNOME_Evolution_Addressbook_Book_cancelOperation(book->priv->corba_book, &ev); if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_add_vcard: Exception adding card to PAS!\n"); + + e_mutex_unlock (op->mutex); + CORBA_exception_free (&ev); - e_book_unqueue_op (book); + + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::cancelOperation call")); return FALSE; } CORBA_exception_free (&ev); - return TRUE; -} - -/* Modifying cards. */ + if (status == E_BOOK_ERROR_OK) { + op->status = E_BOOK_ERROR_CANCELLED; -/** - * e_book_commit_card: - */ -gboolean -e_book_commit_card (EBook *book, - ECard *card, - EBookCallback cb, - gpointer closure) -{ - char *vcard; - gboolean retval; - - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (card != NULL, FALSE); - g_return_val_if_fail (E_IS_CARD (card), FALSE); + pthread_cond_signal (&op->cond); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_commit_card: No URI loaded!\n"); - return FALSE; + rv = TRUE; + } + else { + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_COULD_NOT_CANCEL, + _("e_book_cancel: couldn't cancel")); + rv = FALSE; } - vcard = e_card_get_vcard_assume_utf8 (card); + e_mutex_unlock (op->mutex); - if (vcard == NULL) { - g_warning ("e_book_commit_card: Error " - "getting VCard for card!\n"); - return FALSE; + 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; } - retval = e_book_commit_vcard (book, vcard, cb, closure); + e_mutex_lock (op->mutex); - g_free (vcard); + op->status = status; - e_card_set_book (card, book); + pthread_cond_signal (&op->cond); - return retval; + e_mutex_unlock (op->mutex); } -/** - * e_book_commit_vcard: - */ + + gboolean -e_book_commit_vcard (EBook *book, - const char *vcard, - EBookCallback cb, - gpointer closure) +e_book_remove (EBook *book, + GError **error) { CORBA_Environment ev; + EBookOp *our_op; + EBookStatus status; - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (vcard != NULL, FALSE); + e_return_error_if_fail (book && E_IS_BOOK (book), E_BOOK_ERROR_INVALID_ARG); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_commit_vcard: No URI loaded!\n"); + 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; } - CORBA_exception_init (&ev); + 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_book_queue_op (book, cb, closure, NULL); + our_op = e_book_new_op (book); - GNOME_Evolution_Addressbook_Book_modifyCard ( - book->priv->corba_book, (const GNOME_Evolution_Addressbook_VCard) vcard, &ev); + 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) { - g_warning ("e_book_commit_vcard: Exception " - "modifying card in PAS!\n"); + + e_book_clear_op (book, our_op); + CORBA_exception_free (&ev); - e_book_unqueue_op (book); + + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CORBA_EXCEPTION, + _("Corba exception making Book::remove call")); return FALSE; } CORBA_exception_free (&ev); - return TRUE; + /* 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_check_connection: - */ -gboolean -e_book_check_connection (EBook *book) +static void +e_book_response_remove (EBook *book, + EBookStatus status) { - CORBA_Environment ev; + EBookOp *op; - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); + printf ("e_book_response_remove\n"); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_check_connection: No URI loaded!\n"); - return FALSE; + op = e_book_get_op (book); + + if (op == NULL) { + g_warning ("e_book_response_remove: Cannot find operation "); + return; } - CORBA_exception_init (&ev); + e_mutex_lock (op->mutex); + + op->status = status; + + pthread_cond_signal (&op->cond); + + e_mutex_unlock (op->mutex); +} - GNOME_Evolution_Addressbook_Book_checkConnection (book->priv->corba_book, &ev); + +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_check_connection: Exception " - "querying the PAS!\n"); - CORBA_exception_free (&ev); - return FALSE; + 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; } -guint -e_book_get_cursor (EBook *book, - gchar *query, - EBookCursorCallback cb, - gpointer closure) + + +/** + * 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; - guint tag; - - g_return_val_if_fail (book != NULL, 0); - g_return_val_if_fail (E_IS_BOOK (book), 0); + Bonobo_ServerInfoList *info_list = NULL; + int i; + char *protocol, *query, *colon; + GList *factories = NULL; - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_check_connection: No URI loaded!\n"); - return 0; + colon = strchr (uri, ':'); + if (!colon) { + g_warning ("e_book_load_uri: Unable to determine protocol in the URI\n"); + return FALSE; } - - CORBA_exception_init (&ev); - tag = e_book_queue_op (book, cb, closure, NULL); + 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); - GNOME_Evolution_Addressbook_Book_getCursor (book->priv->corba_book, query, &ev); + info_list = bonobo_activation_query (query, NULL, &ev); if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_all_cards: Exception " - "querying list of cards!\n"); + g_warning ("Eeek! Cannot perform bonobo-activation query for book factories."); CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; + 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); - return tag; + 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; } -guint -e_book_get_book_view (EBook *book, - const gchar *query, - EBookBookViewCallback cb, - gpointer closure) +gboolean +e_book_load_uri (EBook *book, + const char *uri, + gboolean only_if_exists, + GError **error) { - CORBA_Environment ev; - EBookViewListener *listener; - guint tag; - - g_return_val_if_fail (book != NULL, 0); - g_return_val_if_fail (E_IS_BOOK (book), 0); + 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); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_get_book_view: No URI loaded!\n"); - return 0; + /* 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; } - listener = e_book_view_listener_new(); - - CORBA_exception_init (&ev); - tag = e_book_queue_op (book, cb, closure, listener); - - GNOME_Evolution_Addressbook_Book_getBookView (book->priv->corba_book, bonobo_object_corba_objref(BONOBO_OBJECT(listener)), query, &ev); + 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; + } - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_book_view: Exception " - "getting book_view!\n"); CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; + + /* 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; + } } - - CORBA_exception_free (&ev); - return tag; + /* 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; } -guint -e_book_get_completion_view (EBook *book, - const gchar *query, - EBookBookViewCallback cb, - gpointer closure) +gboolean +e_book_load_local_addressbook (EBook *book, + GError **error) { - CORBA_Environment ev; - EBookViewListener *listener; - guint tag; - - g_return_val_if_fail (book != NULL, 0); - g_return_val_if_fail (E_IS_BOOK (book), 0); + char *filename; + char *uri; + gboolean rv; - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_get_completion_view: No URI loaded!\n"); - return 0; - } + filename = g_build_filename (g_get_home_dir(), + "evolution/local/Contacts", + NULL); + uri = g_strdup_printf ("file://%s", filename); - listener = e_book_view_listener_new(); + g_free (filename); - CORBA_exception_init (&ev); - - tag = e_book_queue_op (book, cb, closure, listener); + rv = e_book_load_uri (book, uri, TRUE, error); - GNOME_Evolution_Addressbook_Book_getCompletionView (book->priv->corba_book, - bonobo_object_corba_objref(BONOBO_OBJECT(listener)), - query, &ev); + 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); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_completion_view: Exception " - "getting completion_view!\n"); CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; } - - CORBA_exception_free (&ev); - return tag; + return book->priv->cap; } -guint -e_book_get_changes (EBook *book, - gchar *changeid, - EBookBookViewCallback cb, - gpointer closure) +gboolean +e_book_check_static_capability (EBook *book, + const char *cap) { - CORBA_Environment ev; - EBookViewListener *listener; - guint tag; - - g_return_val_if_fail (book != NULL, 0); - g_return_val_if_fail (E_IS_BOOK (book), 0); + 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; - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_get_changes: No URI loaded!\n"); + 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; } - listener = e_book_view_listener_new(); - - CORBA_exception_init (&ev); +#if notyet + EBook *b; + char *self_uri, *self_uid; - tag = e_book_queue_op (book, cb, closure, listener); - - GNOME_Evolution_Addressbook_Book_getChanges (book->priv->corba_book, bonobo_object_corba_objref(BONOBO_OBJECT(listener)), changeid, &ev); + /* XXX get the setting for the self book and self uid from gconf */ - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_changes: Exception " - "getting changes!\n"); - CORBA_exception_free (&ev); - e_book_unqueue_op (book); - return 0; + b = e_book_new(); + if (! e_book_load_uri (b, self_uri, TRUE, error)) { + g_object_unref (b); + return FALSE; } - - CORBA_exception_free (&ev); - return tag; -} + if (! e_book_get_contact (b, self_uid, + contact, error)) { + g_object_unref (b); + return FALSE; + } -/** - * e_book_cancel - */ + if (book) + *book = b; + else + g_object_unref (b); + return TRUE; +#endif +} -void -e_book_cancel (EBook *book, guint tag) +gboolean +e_book_set_self (EBook *book, const char *id, GError **error) { - g_return_if_fail (book != NULL); - g_return_if_fail (E_IS_BOOK (book)); - g_return_if_fail (tag != 0); - - /* In an attempt to be useful, we take a bit of extra care in reporting - errors. This might come in handy someday. */ - if (tag >= book->priv->op_tag) - g_warning ("Attempt to cancel unassigned operation (%u)", tag); - else if (! e_book_cancel_op (book, tag)) - g_warning ("Attempt to cancel unknown operation (%u)", tag); } -/** - * e_book_get_name: - */ -char * -e_book_get_name (EBook *book) + + +gboolean +e_book_get_default_addressbook (EBook **book, GError **error) { - CORBA_Environment ev; - char *retval; - char *name; + /* XXX for now just load the local ~/evolution/local/Contacts */ + char *path, *uri; + gboolean rv; - g_return_val_if_fail (book != NULL, NULL); - g_return_val_if_fail (E_IS_BOOK (book), NULL); + *book = e_book_new (); - if (book->priv->load_state != URILoaded) { - g_warning ("e_book_get_name: No URI loaded!\n"); - return NULL; - } + path = g_build_filename (g_get_home_dir (), + "evolution/local/Contacts", + NULL); + uri = g_strdup_printf ("file://%s", path); + g_free (path); - CORBA_exception_init (&ev); + rv = e_book_load_uri (*book, uri, FALSE, error); - name = GNOME_Evolution_Addressbook_Book_getName (book->priv->corba_book, &ev); + g_free (uri); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning ("e_book_get_name: Exception getting name from PAS!\n"); - CORBA_exception_free (&ev); - return NULL; + if (!rv) { + g_object_unref (*book); + *book = NULL; } - CORBA_exception_free (&ev); + 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 (name == NULL) { - g_warning ("e_book_get_name: Got NULL name from PAS!\n"); - return NULL; +#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); +} - retval = g_strdup (name); - CORBA_free (name); + - return retval; +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 = URINotLoaded; - book->priv->op_tag = 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 @@ -1598,8 +1967,8 @@ e_book_dispose (GObject *object) book->priv->comp_listener = NULL; } - if (book->priv->load_state == URILoaded) - e_book_unload_uri (book); + if (book->priv->load_state == E_BOOK_URI_LOADED) + e_book_unload_uri (book, NULL); CORBA_exception_init (&ev); @@ -1640,16 +2009,6 @@ e_book_class_init (EBookClass *klass) parent_class = g_type_class_ref (G_TYPE_OBJECT); - e_book_signals [LINK_STATUS] = - g_signal_new ("link_status", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EBookClass, link_status), - NULL, NULL, - e_book_marshal_NONE__BOOL, - G_TYPE_NONE, 1, - G_TYPE_BOOLEAN); - e_book_signals [WRITABLE_STATUS] = g_signal_new ("writable_status", G_OBJECT_CLASS_TYPE (object_class), |