aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addressbook/ChangeLog77
-rw-r--r--addressbook/backend/ebook/e-book-listener.c4
-rw-r--r--addressbook/backend/ebook/e-book-types.h4
-rw-r--r--addressbook/backend/idl/addressbook.idl4
-rw-r--r--addressbook/backend/pas/pas-backend-file.c93
-rw-r--r--addressbook/backend/pas/pas-backend-ldap.c91
-rw-r--r--addressbook/backend/pas/pas-backend.c87
-rw-r--r--addressbook/backend/pas/pas-backend.h15
-rw-r--r--addressbook/backend/pas/pas-book-factory.c221
-rw-r--r--addressbook/backend/pas/pas-book-factory.h6
10 files changed, 511 insertions, 91 deletions
diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog
index 40d4cc9ae7..6a025eb26b 100644
--- a/addressbook/ChangeLog
+++ b/addressbook/ChangeLog
@@ -1,3 +1,80 @@
+2000-04-30 Federico Mena Quintero <federico@helixcode.com>
+
+ * backend/ebook/e-book-types.h (EBookStatus): Added new status
+ values for the IDL stuff.
+
+ * backend/pas/pas-book-factory.h (PASBookFactoryClass): New
+ "last_book_gone" signal.
+
+ * backend/pas/pas-book-factory.c
+ (pas_book_factory_launch_backend): Better error handling.
+ (pas_book_factory_process_queue): Let
+ pas_book_factory_process_request() free the request.
+ (pas_book_factory_process_request): Free the request here.
+ Perform better error handling.
+ (free_active_server_map_entry): Free an active server map entry;
+ free the URI key and unref the backend value. This function was
+ renamed; the old one was trying to CORBA_Object_unref() a GTK+
+ object!
+ (remove_backends_entry): Free a backend table entry; free the URI
+ key.
+ (backend_last_client_gone_cb): Remove the backend from the active
+ server map and emit the "last_book_gone" signal if appropriate.
+ (pas_book_factory_get_n_backends): New function to query the
+ number of running backends in an addressbook factory.
+
+ * backend/idl/addressbook.idl (BookListener::CallStatus): Added a
+ ProtocolNotSupported code. This is for when the addressbook
+ factory cannot find a provider for the requested URI.
+
+ * backend/pas/pas-backend.h (PASBackendClass): New
+ "last_client_gone" signal.
+ (PASBackendClass): New get_uri virtual method.
+
+ * backend/pas/pas-backend.c (pas_backend_load_uri): Return a
+ gboolean success code.
+ (pas_backend_add_client): Return a gboolean success code.
+ (pas_backend_last_client_gone): New function used by backend
+ implementations to notify upwards when the backend's last client
+ is destroyed.
+ (pas_backend_get_uri): New function to get the URI of a backend.
+
+ * backend/pas/pas-backend-file.c (pas_backend_file_add_client):
+ Pass the backend as the closure data to the "destroy" handler of
+ the book. We cannot call pas_book_get_backend() in the callback
+ since the book's private data has already been destroyed when the
+ callback is invoked. Alternatively, we could move the private
+ data destruction step to the book's ::finalize() method.
+ (pas_backend_file_book_destroy_cb): Get the backend from the
+ callback's data, not from the book.
+ (pas_backend_file_remove_client): Remove the book from the list of
+ clients. When all clients go away, call
+ pas_backend_last_client_gone().
+ (PASBackendFilePrivate): Added an uri field.
+ (pas_backend_file_get_uri): Implement the get_uri method.
+ (pas_backend_file_load_uri): Return a gboolean success code.
+ Also, store the URI in the private structure.
+ (pas_backend_file_add_client): Return a gboolean success code.
+ Also, call pas_backend_last_client_gone() if appropriate.
+ (pas_backend_file_destroy): Free the bf->priv->uri.
+
+ * backend/pas/pas-backend-ldap.c (pas_backend_ldap_add_client):
+ Pass the backend as the closure data to the "destroy" handler of
+ the book. See above for rationale.
+ (pas_backend_ldap_book_destroy_cb): Get the backend from the
+ callback's data.
+ (pas_backend_ldap_remove_client): Remove the book from the list of
+ clients. When all clients go away, call
+ pas_backend_last_client_gone().
+ (pas_backend_ldap_load_uri): Return a gboolean success code.
+ (pas_backend_ldap_add_client): Return a gboolean success code.
+ Also, call pas_backend_last_client_gone() if appropriate.
+ (PASBackendLDAPPrivate): New uri field.
+ (pas_backend_ldap_get_uri): Implement the get_uri method.
+ (pas_backend_ldap_load_uri): Store the uri in the private
+ structure.
+ (pas_backend_ldap_destroy): Free the bl->priv->uri.
+
2000-04-30 Chris Toshok <toshok@helixcode.com>
* gui/component/Makefile.am (evolution_addressbook_SOURCES): added
diff --git a/addressbook/backend/ebook/e-book-listener.c b/addressbook/backend/ebook/e-book-listener.c
index d0ccc46944..5efdaa37df 100644
--- a/addressbook/backend/ebook/e-book-listener.c
+++ b/addressbook/backend/ebook/e-book-listener.c
@@ -357,6 +357,10 @@ e_book_listener_convert_status (const Evolution_BookListener_CallStatus status)
return E_BOOK_STATUS_PERMISSION_DENIED;
case Evolution_BookListener_CardNotFound:
return E_BOOK_STATUS_CARD_NOT_FOUND;
+ case Evolution_BookListener_ProtocolNotSupported:
+ return E_BOOK_STATUS_PROTOCOL_NOT_SUPPORTED;
+ case Evolution_BookListener_OtherError:
+ return E_BOOK_STATUS_OTHER_ERROR;
default:
g_warning ("e_book_listener_convert_status: Unknown status "
"from card server: %d\n", (int) status);
diff --git a/addressbook/backend/ebook/e-book-types.h b/addressbook/backend/ebook/e-book-types.h
index 1d86a5bd4d..a3e8bbc032 100644
--- a/addressbook/backend/ebook/e-book-types.h
+++ b/addressbook/backend/ebook/e-book-types.h
@@ -21,7 +21,9 @@ typedef enum {
E_BOOK_STATUS_UNKNOWN,
E_BOOK_STATUS_REPOSITORY_OFFLINE,
E_BOOK_STATUS_PERMISSION_DENIED,
- E_BOOK_STATUS_CARD_NOT_FOUND
+ E_BOOK_STATUS_CARD_NOT_FOUND,
+ E_BOOK_STATUS_PROTOCOL_NOT_SUPPORTED,
+ E_BOOK_STATUS_OTHER_ERROR
} EBookStatus;
END_GNOME_DECLS
diff --git a/addressbook/backend/idl/addressbook.idl b/addressbook/backend/idl/addressbook.idl
index 44987a76fb..b3bcb5ea87 100644
--- a/addressbook/backend/idl/addressbook.idl
+++ b/addressbook/backend/idl/addressbook.idl
@@ -80,7 +80,9 @@ module Evolution {
Success,
RepositoryOffline,
PermissionDenied,
- CardNotFound
+ CardNotFound,
+ ProtocolNotSupported,
+ OtherError
};
void respond_create_card (in CallStatus status, in CardId Id);
diff --git a/addressbook/backend/pas/pas-backend-file.c b/addressbook/backend/pas/pas-backend-file.c
index 20de42808c..e2ca9a7348 100644
--- a/addressbook/backend/pas/pas-backend-file.c
+++ b/addressbook/backend/pas/pas-backend-file.c
@@ -33,6 +33,7 @@ typedef struct _PASBackendFileSearchContext PASBackendFileSearchContext;
struct _PASBackendFilePrivate {
GList *clients;
gboolean loaded;
+ char *uri;
DB *file_db;
GList *book_views;
};
@@ -825,11 +826,11 @@ pas_backend_file_process_client_requests (PASBook *book)
}
static void
-pas_backend_file_book_destroy_cb (PASBook *book)
+pas_backend_file_book_destroy_cb (PASBook *book, gpointer data)
{
PASBackendFile *backend;
- backend = PAS_BACKEND_FILE (pas_book_get_backend (book));
+ backend = PAS_BACKEND_FILE (data);
pas_backend_remove_client (PAS_BACKEND (backend), book);
}
@@ -924,7 +925,7 @@ pas_backend_file_maybe_upgrade_db (PASBackendFile *bf)
return ret_val;
}
-static void
+static gboolean
pas_backend_file_load_uri (PASBackend *backend,
const char *uri)
{
@@ -937,18 +938,45 @@ pas_backend_file_load_uri (PASBackend *backend,
bf->priv->file_db = dbopen (filename, O_RDWR | O_CREAT, 0666, DB_HASH, NULL);
+ g_free (filename);
+
if (bf->priv->file_db != NULL) {
if (pas_backend_file_maybe_upgrade_db (bf))
bf->priv->loaded = TRUE;
/* XXX what if we fail to upgrade it? */
+
+ bf->priv->uri = g_strdup (uri);
+ } else {
+ GList *l;
+
+ for (l = bf->priv->clients; l; l = l->next) {
+ PASBook *book;
+
+ book = PAS_BOOK (l->data);
+ pas_book_respond_open (book, Evolution_BookListener_OtherError);
+ }
+
+ return FALSE;
}
- else
- g_warning ("pas_backend_file_load_uri failed for '%s'\n", filename);
- g_free (filename);
+ return TRUE;
}
-static void
+/* Get_uri handler for the addressbook file backend */
+static const char *
+pas_backend_file_get_uri (PASBackend *backend)
+{
+ PASBackendFile *bf;
+
+ bf = PAS_BACKEND_FILE (backend);
+
+ g_return_val_if_fail (bf->priv->loaded, NULL);
+ g_assert (bf->priv->uri != NULL);
+
+ return bf->priv->uri;
+}
+
+static gboolean
pas_backend_file_add_client (PASBackend *backend,
Evolution_BookListener listener)
{
@@ -964,10 +992,15 @@ pas_backend_file_add_client (PASBackend *backend,
backend, listener,
pas_backend_file_get_vcard);
- g_assert (book != NULL);
+ if (!book) {
+ if (!bf->priv->clients)
+ pas_backend_last_client_gone (backend);
+
+ return FALSE;
+ }
gtk_signal_connect (GTK_OBJECT (book), "destroy",
- pas_backend_file_book_destroy_cb, NULL);
+ pas_backend_file_book_destroy_cb, backend);
gtk_signal_connect (GTK_OBJECT (book), "requests_queued",
pas_backend_file_process_client_requests, NULL);
@@ -983,18 +1016,46 @@ pas_backend_file_add_client (PASBackend *backend,
pas_book_respond_open (
book, Evolution_BookListener_Success);
}
+
+ return TRUE;
}
static void
pas_backend_file_remove_client (PASBackend *backend,
PASBook *book)
{
+ PASBackendFile *bf;
+ GList *l;
+ PASBook *lbook;
+
g_return_if_fail (backend != NULL);
- g_return_if_fail (PAS_IS_BACKEND (backend));
+ g_return_if_fail (PAS_IS_BACKEND_FILE (backend));
g_return_if_fail (book != NULL);
g_return_if_fail (PAS_IS_BOOK (book));
- g_warning ("pas_backend_file_remove_client: Unimplemented!\n");
+ bf = PAS_BACKEND_FILE (backend);
+
+ /* Find the book in the list of clients */
+
+ for (l = bf->priv->clients; l; l = l->next) {
+ lbook = PAS_BOOK (l->data);
+
+ if (lbook == book)
+ break;
+ }
+
+ g_assert (l != NULL);
+
+ /* Disconnect */
+
+ bf->priv->clients = g_list_remove_link (bf->priv->clients, l);
+ g_list_free_1 (l);
+
+ /* When all clients go away, notify the parent factory about it so that
+ * it may decide whether to kill the backend or not.
+ */
+ if (!bf->priv->clients)
+ pas_backend_last_client_gone (backend);
}
static gboolean
@@ -1031,6 +1092,15 @@ pas_backend_file_new (void)
static void
pas_backend_file_destroy (GtkObject *object)
{
+ PASBackendFile *bf;
+
+ bf = PAS_BACKEND_FILE (object);
+
+ if (bf->priv->uri) {
+ g_free (bf->priv->uri);
+ bf->priv->uri = NULL;
+ }
+
GTK_OBJECT_CLASS (pas_backend_file_parent_class)->destroy (object);
}
@@ -1046,6 +1116,7 @@ pas_backend_file_class_init (PASBackendFileClass *klass)
/* Set the virtual methods. */
parent_class->load_uri = pas_backend_file_load_uri;
+ parent_class->get_uri = pas_backend_file_get_uri;
parent_class->add_client = pas_backend_file_add_client;
parent_class->remove_client = pas_backend_file_remove_client;
diff --git a/addressbook/backend/pas/pas-backend-ldap.c b/addressbook/backend/pas/pas-backend-ldap.c
index 82694510ba..f1fa1ea92b 100644
--- a/addressbook/backend/pas/pas-backend-ldap.c
+++ b/addressbook/backend/pas/pas-backend-ldap.c
@@ -29,6 +29,7 @@ typedef struct _PASBackendLDAPCursorPrivate PASBackendLDAPCursorPrivate;
typedef struct _PASBackendLDAPBookView PASBackendLDAPBookView;
struct _PASBackendLDAPPrivate {
+ char *uri;
gboolean connected;
GList *clients;
LDAP *ldap;
@@ -842,11 +843,11 @@ pas_backend_ldap_process_client_requests (PASBook *book)
}
static void
-pas_backend_ldap_book_destroy_cb (PASBook *book)
+pas_backend_ldap_book_destroy_cb (PASBook *book, gpointer data)
{
PASBackendLDAP *backend;
- backend = PAS_BACKEND_LDAP (pas_book_get_backend (book));
+ backend = PAS_BACKEND_LDAP (data);
pas_backend_remove_client (PAS_BACKEND (backend), book);
}
@@ -863,7 +864,7 @@ pas_backend_ldap_get_vcard (PASBook *book, const char *id)
/* XXX use ldap_search */
- if (LDAP_SUCCESS == ldap_error) {
+ if (ldap_error == LDAP_SUCCESS) {
/* success */
return g_strdup ("");
}
@@ -872,7 +873,7 @@ pas_backend_ldap_get_vcard (PASBook *book, const char *id)
}
}
-static void
+static gboolean
pas_backend_ldap_load_uri (PASBackend *backend,
const char *uri)
{
@@ -883,7 +884,8 @@ pas_backend_ldap_load_uri (PASBackend *backend,
g_assert (bl->priv->connected == FALSE);
ldap_error = ldap_url_parse ((char*)uri, &lud);
- if (LDAP_SUCCESS == ldap_error) {
+ if (ldap_error == LDAP_SUCCESS) {
+ bl->priv->uri = g_strdup (uri);
bl->priv->ldap_host = g_strdup(lud->lud_host);
bl->priv->ldap_port = lud->lud_port;
bl->priv->ldap_rootdn = g_strdup(lud->lud_dn);
@@ -891,14 +893,32 @@ pas_backend_ldap_load_uri (PASBackend *backend,
ldap_free_urldesc(lud);
pas_backend_ldap_ensure_connected(bl);
- }
- else {
- g_warning ("pas_backend_ldap_load_uri failed for '%s' (error %s)\n",
- uri, ldap_err2string(ldap_error));
+ return TRUE;
+ } else {
+ GList *l;
+
+ for (l = bl->priv->clients; l; l = l->next) {
+ PASBook *book;
+
+ book = PAS_BOOK (l->data);
+ pas_book_respond_open (book, Evolution_BookListener_OtherError);
+ }
+
+ return FALSE;
}
}
-static void
+/* Get_uri handler for the addressbook LDAP backend */
+static const char *
+pas_backend_ldap_get_uri (PASBackend *backend)
+{
+ PASBackendLDAP *bl;
+
+ bl = PAS_BACKEND_LDAP (backend);
+ return bl->priv->uri;
+}
+
+static gboolean
pas_backend_ldap_add_client (PASBackend *backend,
Evolution_BookListener listener)
{
@@ -914,10 +934,15 @@ pas_backend_ldap_add_client (PASBackend *backend,
backend, listener,
pas_backend_ldap_get_vcard);
- g_assert (book != NULL);
+ if (!book) {
+ if (!bl->priv->clients)
+ pas_backend_last_client_gone (backend);
+
+ return FALSE;
+ }
gtk_signal_connect (GTK_OBJECT (book), "destroy",
- pas_backend_ldap_book_destroy_cb, NULL);
+ pas_backend_ldap_book_destroy_cb, backend);
gtk_signal_connect (GTK_OBJECT (book), "requests_queued",
pas_backend_ldap_process_client_requests, NULL);
@@ -933,18 +958,46 @@ pas_backend_ldap_add_client (PASBackend *backend,
pas_book_respond_open (
book, Evolution_BookListener_Success);
}
+
+ return TRUE;
}
static void
pas_backend_ldap_remove_client (PASBackend *backend,
PASBook *book)
{
+ PASBackendLDAP *bl;
+ GList *l;
+ PASBook *lbook;
+
g_return_if_fail (backend != NULL);
- g_return_if_fail (PAS_IS_BACKEND (backend));
+ g_return_if_fail (PAS_IS_BACKEND_LDAP (backend));
g_return_if_fail (book != NULL);
g_return_if_fail (PAS_IS_BOOK (book));
- g_warning ("pas_backend_ldap_remove_client: Unimplemented!\n");
+ bl = PAS_BACKEND_LDAP (backend);
+
+ /* Find the book in the list of clients */
+
+ for (l = bl->priv->clients, l; l = l->next) {
+ lbook = PAS_BOOK (l->data);
+
+ if (lbook == book)
+ break;
+ }
+
+ g_assert (l != NULL);
+
+ /* Disconnect */
+
+ bl->priv->clients = g_list_remove_link (bl->priv->clients, l);
+ g_list_free_1 (l);
+
+ /* When all clients go away, notify the parent factory about it so that
+ * it may decide whether to kill the backend or not.
+ */
+ if (!bl->priv->clients)
+ pas_backend_last_client_gone (backend);
}
static gboolean
@@ -983,6 +1036,15 @@ pas_backend_ldap_new (void)
static void
pas_backend_ldap_destroy (GtkObject *object)
{
+ PASBackendLDAP *bl;
+
+ bl = PAS_BACKEND_LDAP (object);
+
+ if (bl->priv->uri) {
+ g_free (bl->priv->uri);
+ bl->priv->uri = NULL;
+ }
+
GTK_OBJECT_CLASS (pas_backend_ldap_parent_class)->destroy (object);
}
@@ -998,6 +1060,7 @@ pas_backend_ldap_class_init (PASBackendLDAPClass *klass)
/* Set the virtual methods. */
parent_class->load_uri = pas_backend_ldap_load_uri;
+ parent_class->get_uri = pas_backend_ldap_get_uri;
parent_class->add_client = pas_backend_ldap_add_client;
parent_class->remove_client = pas_backend_ldap_remove_client;
diff --git a/addressbook/backend/pas/pas-backend.c b/addressbook/backend/pas/pas-backend.c
index d8aa23fb37..3d315c2faf 100644
--- a/addressbook/backend/pas/pas-backend.c
+++ b/addressbook/backend/pas/pas-backend.c
@@ -7,45 +7,78 @@
#include <config.h>
#include <gtk/gtkobject.h>
+#include <gtk/gtksignal.h>
#include "pas-backend.h"
#define CLASS(o) PAS_BACKEND_CLASS (GTK_OBJECT (o)->klass)
+/* Signal IDs */
+enum {
+ LAST_CLIENT_GONE,
+ LAST_SIGNAL
+};
+
+static guint pas_backend_signals[LAST_SIGNAL];
+
+
gboolean
pas_backend_construct (PASBackend *backend)
{
return TRUE;
}
-void
+gboolean
pas_backend_load_uri (PASBackend *backend,
const char *uri)
{
- g_return_if_fail (backend != NULL);
- g_return_if_fail (PAS_IS_BACKEND (backend));
- g_return_if_fail (uri != NULL);
+ g_return_val_if_fail (backend != NULL, FALSE);
+ g_return_val_if_fail (PAS_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
g_assert (CLASS (backend)->load_uri != NULL);
- CLASS (backend)->load_uri (backend, uri);
+ return (* CLASS (backend)->load_uri) (backend, uri);
+}
+
+/**
+ * pas_backend_get_uri:
+ * @backend: An addressbook backend.
+ *
+ * Queries the URI that an addressbook backend is serving.
+ *
+ * Return value: URI for the backend.
+ **/
+const char *
+pas_backend_get_uri (PASBackend *backend)
+{
+ g_return_val_if_fail (backend != NULL, NULL);
+ g_return_val_if_fail (PAS_IS_BACKEND (backend), NULL);
+
+ g_assert (CLASS (backend)->get_uri != NULL);
+
+ return (* CLASS (backend)->get_uri) (backend);
}
/**
* pas_backend_add_client:
- * @backend:
- * @listener:
+ * @backend: An addressbook backend.
+ * @listener: Listener for notification to the client.
+ *
+ * Adds a client to an addressbook backend.
+ *
+ * Return value: TRUE on success, FALSE on failure to add the client.
*/
-void
+gboolean
pas_backend_add_client (PASBackend *backend,
Evolution_BookListener listener)
{
- g_return_if_fail (backend != NULL);
- g_return_if_fail (PAS_IS_BACKEND (backend));
- g_return_if_fail (listener != CORBA_OBJECT_NIL);
+ g_return_val_if_fail (backend != NULL, FALSE);
+ g_return_val_if_fail (PAS_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (listener != CORBA_OBJECT_NIL, FALSE);
g_assert (CLASS (backend)->add_client != NULL);
- CLASS (backend)->add_client (backend, listener);
+ return CLASS (backend)->add_client (backend, listener);
}
void
@@ -62,6 +95,23 @@ pas_backend_remove_client (PASBackend *backend,
CLASS (backend)->remove_client (backend, book);
}
+/**
+ * pas_backend_last_client_gone:
+ * @backend: An addressbook backend.
+ *
+ * Emits the "last_client_gone" signal for the specified backend. Should
+ * only be called from backend implementations if the backend really does
+ * not have any more clients.
+ **/
+void
+pas_backend_last_client_gone (PASBackend *backend)
+{
+ g_return_if_fail (backend != NULL);
+ g_return_if_fail (PAS_IS_BACKEND (backend));
+
+ gtk_signal_emit (GTK_OBJECT (backend), pas_backend_signals[LAST_CLIENT_GONE]);
+}
+
static void
pas_backend_init (PASBackend *backend)
{
@@ -70,6 +120,19 @@ pas_backend_init (PASBackend *backend)
static void
pas_backend_class_init (PASBackendClass *klass)
{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass *) klass;
+
+ pas_backend_signals[LAST_CLIENT_GONE] =
+ gtk_signal_new ("last_client_gone",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (PASBackendClass, last_client_gone),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, pas_backend_signals, LAST_SIGNAL);
}
/**
diff --git a/addressbook/backend/pas/pas-backend.h b/addressbook/backend/pas/pas-backend.h
index 46cce03e03..a1d1a291fd 100644
--- a/addressbook/backend/pas/pas-backend.h
+++ b/addressbook/backend/pas/pas-backend.h
@@ -39,21 +39,28 @@ typedef struct {
GtkObjectClass parent_class;
/* Virtual methods */
- void (*load_uri) (PASBackend *backend, const char *uri);
- void (*add_client) (PASBackend *backend, Evolution_BookListener listener);
+ gboolean (*load_uri) (PASBackend *backend, const char *uri);
+ const char *(* get_uri) (PASBackend *backend);
+ gboolean (*add_client) (PASBackend *backend, Evolution_BookListener listener);
void (*remove_client) (PASBackend *backend, PASBook *book);
+
+ /* Notification signals */
+ void (* last_client_gone) (PASBackend *backend);
} PASBackendClass;
typedef PASBackend * (*PASBackendFactoryFn) (void);
gboolean pas_backend_construct (PASBackend *backend);
-void pas_backend_load_uri (PASBackend *backend,
+gboolean pas_backend_load_uri (PASBackend *backend,
const char *uri);
-void pas_backend_add_client (PASBackend *backend,
+const char *pas_backend_get_uri (PASBackend *backend);
+gboolean pas_backend_add_client (PASBackend *backend,
Evolution_BookListener listener);
void pas_backend_remove_client (PASBackend *backend,
PASBook *book);
+void pas_backend_last_client_gone (PASBackend *backend);
+
GtkType pas_backend_get_type (void);
#define PAS_BACKEND_TYPE (pas_backend_get_type ())
diff --git a/addressbook/backend/pas/pas-book-factory.c b/addressbook/backend/pas/pas-book-factory.c
index 77cc269f19..aec28c650e 100644
--- a/addressbook/backend/pas/pas-book-factory.c
+++ b/addressbook/backend/pas/pas-book-factory.c
@@ -35,6 +35,14 @@ struct _PASBookFactoryPrivate {
GList *queued_requests;
};
+/* Signal IDs */
+enum {
+ LAST_BOOK_GONE,
+ LAST_SIGNAL
+};
+
+static guint factory_signals[LAST_SIGNAL];
+
static char *
pas_book_factory_canonicalize_uri (const char *uri)
{
@@ -77,8 +85,6 @@ pas_book_factory_register_backend (PASBookFactory *factory,
g_return_if_fail (proto != NULL);
g_return_if_fail (backend != NULL);
-
-
if (g_hash_table_lookup (factory->priv->backends, proto) != NULL) {
g_warning ("pas_book_factory_register_backend: "
"Proto \"%s\" already registered!\n", proto);
@@ -88,11 +94,62 @@ pas_book_factory_register_backend (PASBookFactory *factory,
g_strdup (proto), backend);
}
+/**
+ * pas_book_factory_get_n_backends:
+ * @factory: An addressbook factory.
+ *
+ * Queries the number of running addressbook backends in an addressbook factory.
+ *
+ * Return value: Number of running backends.
+ **/
+int
+pas_book_factory_get_n_backends (PASBookFactory *factory)
+{
+ g_return_val_if_fail (factory != NULL, -1);
+ g_return_val_if_fail (PAS_IS_BOOK_FACTORY (factory), -1);
+
+ return g_hash_table_size (factory->priv->active_server_map);
+}
+
+/* Callback used when a backend loses its last connected client */
+static void
+backend_last_client_gone_cb (PASBackend *backend, gpointer data)
+{
+ PASBookFactory *factory;
+ const char *uri;
+ gpointer orig_key;
+ gboolean result;
+ char *orig_uri;
+
+ factory = PAS_BOOK_FACTORY (data);
+
+ /* Remove the backend from the active server map */
+
+ uri = pas_backend_get_uri (backend);
+ g_assert (uri != NULL);
+
+ result = g_hash_table_lookup_extended (factory->priv->active_server_map, uri,
+ &orig_key, NULL);
+ g_assert (result != FALSE);
+
+ orig_uri = orig_key;
+
+ g_hash_table_remove (factory->priv->active_server_map, orig_uri);
+ g_free (orig_uri);
+
+ gtk_object_unref (GTK_OBJECT (backend));
+
+ /* Notify upstream if there are no more backends */
+
+ if (g_hash_table_size (factory->priv->active_server_map) == 0)
+ gtk_signal_emit (GTK_OBJECT (factory), factory_signals[LAST_BOOK_GONE]);
+}
+
static PASBackendFactoryFn
pas_book_factory_lookup_backend_factory (PASBookFactory *factory,
const char *uri)
{
- PASBackendFactoryFn backend;
+ PASBackendFactoryFn backend_fn;
char *proto;
char *canonical_uri;
@@ -110,32 +167,70 @@ pas_book_factory_lookup_backend_factory (PASBookFactory *factory,
return NULL;
}
- backend = g_hash_table_lookup (factory->priv->backends, proto);
+ backend_fn = g_hash_table_lookup (factory->priv->backends, proto);
g_free (proto);
g_free (canonical_uri);
- return backend;
+ return backend_fn;
}
static PASBackend *
pas_book_factory_launch_backend (PASBookFactory *factory,
- PASBookFactoryQueuedRequest *request)
+ Evolution_BookListener listener,
+ const char *uri)
{
PASBackendFactoryFn backend_factory;
PASBackend *backend;
backend_factory = pas_book_factory_lookup_backend_factory (
- factory, request->uri);
- g_assert (backend_factory != NULL);
+ factory, uri);
+
+ if (!backend_factory) {
+ CORBA_Environment ev;
+
+ CORBA_exception_init (&ev);
+ Evolution_BookListener_respond_open_book (
+ listener,
+ Evolution_BookListener_ProtocolNotSupported,
+ CORBA_OBJECT_NIL,
+ &ev);
- backend = (backend_factory) ();
- g_assert (backend != NULL);
+ if (ev._major != CORBA_NO_EXCEPTION)
+ g_message ("pas_book_factory_launch_backend(): could not notify "
+ "the listener");
+
+ CORBA_exception_free (&ev);
+ return NULL;
+ }
+
+ backend = (* backend_factory) ();
+ if (!backend) {
+ CORBA_Environment ev;
+
+ CORBA_exception_init (&ev);
+ Evolution_BookListener_respond_open_book (
+ listener,
+ Evolution_BookListener_OtherError,
+ CORBA_OBJECT_NIL,
+ &ev);
+
+ if (ev._major != CORBA_NO_EXCEPTION)
+ g_message ("pas_book_factory_launch_backend(): could not notify "
+ "the listener");
+
+ CORBA_exception_free (&ev);
+ return NULL;
+ }
g_hash_table_insert (factory->priv->active_server_map,
- g_strdup (request->uri),
+ g_strdup (uri),
backend);
+ gtk_signal_connect (GTK_OBJECT (backend), "last_client_gone",
+ backend_last_client_gone_cb,
+ factory);
+
return backend;
}
@@ -144,24 +239,43 @@ pas_book_factory_process_request (PASBookFactory *factory,
PASBookFactoryQueuedRequest *request)
{
PASBackend *backend;
+ char *uri;
+ Evolution_BookListener listener;
+ CORBA_Environment ev;
- request = factory->priv->queued_requests->data;
+ uri = request->uri;
+ listener = request->listener;
+ g_free (request);
- backend = g_hash_table_lookup (factory->priv->active_server_map, request->uri);
+ /* Look up the backend and create one if needed */
- if (backend == NULL) {
+ backend = g_hash_table_lookup (factory->priv->active_server_map, uri);
- backend = pas_book_factory_launch_backend (factory, request);
- pas_backend_add_client (backend, request->listener);
- pas_backend_load_uri (backend, request->uri);
- g_free (request->uri);
+ if (!backend) {
+ backend = pas_book_factory_launch_backend (factory, listener, uri);
+ if (!backend)
+ goto out;
- return;
+ if (!pas_backend_add_client (backend, listener))
+ goto out;
+
+ pas_backend_load_uri (backend, uri);
+
+ goto out;
}
- g_free (request->uri);
+ pas_backend_add_client (backend, listener);
+
+ out:
+ g_free (uri);
- pas_backend_add_client (backend, request->listener);
+ CORBA_exception_init (&ev);
+ CORBA_Object_release (listener, &ev);
+
+ if (ev._major != CORBA_NO_EXCEPTION)
+ g_message ("pas_book_factory_process_request(): could not release the listener");
+
+ CORBA_exception_free (&ev);
}
static gboolean
@@ -170,15 +284,16 @@ pas_book_factory_process_queue (PASBookFactory *factory)
/* Process pending Book-creation requests. */
if (factory->priv->queued_requests != NULL) {
PASBookFactoryQueuedRequest *request;
+ GList *l;
- request = factory->priv->queued_requests->data;
+ l = factory->priv->queued_requests;
+ request = l->data;
pas_book_factory_process_request (factory, request);
- factory->priv->queued_requests = g_list_remove (
- factory->priv->queued_requests, request);
-
- g_free (request);
+ factory->priv->queued_requests = g_list_remove_link (
+ factory->priv->queued_requests, l);
+ g_list_free_1 (l);
}
if (factory->priv->queued_requests == NULL) {
@@ -343,7 +458,6 @@ register_factory (CORBA_Object obj)
int ret;
CORBA_exception_init (&ev);
-
ret = goad_server_register (NULL, obj, PAS_BOOK_FACTORY_GOAD_ID, "server", &ev);
if (ev._major != CORBA_NO_EXCEPTION) {
@@ -394,27 +508,26 @@ pas_book_factory_init (PASBookFactory *factory)
factory->priv->queued_requests = NULL;
}
-static gboolean
-pas_book_factory_remove_asm_entry (gpointer key, gpointer value,
- gpointer data)
+static void
+free_active_server_map_entry (gpointer key, gpointer value, gpointer data)
{
- CORBA_Environment ev;
+ char *uri;
+ PASBackend *backend;
- g_free (key);
+ uri = key;
+ g_free (uri);
- CORBA_exception_init (&ev);
- CORBA_Object_release ((CORBA_Object) value, &ev);
- CORBA_exception_free (&ev);
-
- return TRUE;
+ backend = PAS_BACKEND (value);
+ gtk_object_unref (GTK_OBJECT (backend));
}
-static gboolean
-pas_book_factory_remove_backend_entry (gpointer key, gpointer value,
- gpointer data)
+static void
+remove_backends_entry (gpointer key, gpointer value, gpointer data)
{
- g_free (key);
- return TRUE;
+ char *uri;
+
+ uri = key;
+ g_free (uri);
}
static void
@@ -438,15 +551,17 @@ pas_book_factory_destroy (GtkObject *object)
g_list_free (factory->priv->queued_requests);
factory->priv->queued_requests = NULL;
- g_hash_table_foreach_remove (factory->priv->active_server_map,
- pas_book_factory_remove_asm_entry,
- NULL);
+ g_hash_table_foreach (factory->priv->active_server_map,
+ free_active_server_map_entry,
+ NULL);
g_hash_table_destroy (factory->priv->active_server_map);
+ factory->priv->active_server_map = NULL;
- g_hash_table_foreach_remove (factory->priv->backends,
- pas_book_factory_remove_backend_entry,
- NULL);
+ g_hash_table_foreach (factory->priv->backends,
+ remove_backends_entry,
+ NULL);
g_hash_table_destroy (factory->priv->backends);
+ factory->priv->backends = NULL;
g_free (factory->priv);
@@ -480,6 +595,16 @@ pas_book_factory_class_init (PASBookFactoryClass *klass)
pas_book_factory_parent_class = gtk_type_class (bonobo_object_get_type ());
+ factory_signals[LAST_BOOK_GONE] =
+ gtk_signal_new ("last_book_gone",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (PASBookFactoryClass, last_book_gone),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ gtk_object_class_add_signals (object_class, factory_signals, LAST_SIGNAL);
+
object_class->destroy = pas_book_factory_destroy;
pas_book_factory_corba_class_init ();
diff --git a/addressbook/backend/pas/pas-book-factory.h b/addressbook/backend/pas/pas-book-factory.h
index c9607298f5..7e4690adf1 100644
--- a/addressbook/backend/pas/pas-book-factory.h
+++ b/addressbook/backend/pas/pas-book-factory.h
@@ -21,6 +21,10 @@ typedef struct {
typedef struct {
BonoboObjectClass parent_class;
+
+ /* Notification signals */
+
+ void (* last_book_gone) (PASBookFactory *factory);
} PASBookFactoryClass;
PASBookFactory *pas_book_factory_new (void);
@@ -29,6 +33,8 @@ void pas_book_factory_register_backend (PASBookFactory
const char *proto,
PASBackendFactoryFn backend_factory);
+int pas_book_factory_get_n_backends (PASBookFactory *factory);
+
void pas_book_factory_activate (PASBookFactory *factory);
GtkType pas_book_factory_get_type (void);