X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libedataserver%2Fe-source-registry.c;h=5d2c5539db26b3c8d3e8b28298cc189353d6e1f0;hb=78340f9274d5c2ee0f52507f935f97d6b92f513e;hp=df2c3e5a38318331f54978b769ca316887328d5a;hpb=be7e0e49bba18e6f46d50394df0cd28f45e71379;p=platform%2Fupstream%2Fevolution-data-server.git diff --git a/libedataserver/e-source-registry.c b/libedataserver/e-source-registry.c index df2c3e5..5d2c553 100644 --- a/libedataserver/e-source-registry.c +++ b/libedataserver/e-source-registry.c @@ -89,6 +89,7 @@ typedef struct _AsyncContext AsyncContext; typedef struct _AuthContext AuthContext; +typedef struct _CreateContext CreateContext; typedef struct _SourceClosure SourceClosure; typedef struct _ThreadClosure ThreadClosure; @@ -102,10 +103,10 @@ struct _ESourceRegistryPrivate { EDBusSourceManager *dbus_source_manager; GHashTable *object_path_table; - GMutex *object_path_table_lock; + GMutex object_path_table_lock; GHashTable *sources; - GMutex *sources_lock; + GMutex sources_lock; GSettings *settings; }; @@ -129,6 +130,13 @@ struct _AuthContext { GError **error; }; +/* Used in e_source_registry_create_sources_sync() */ +struct _CreateContext { + GHashTable *pending_uids; + GMainContext *main_context; + GMainLoop *main_loop; +}; + struct _SourceClosure { ESourceRegistry *registry; ESource *source; @@ -138,8 +146,9 @@ struct _ThreadClosure { ESourceRegistry *registry; GMainContext *main_context; GMainLoop *main_loop; - GCond *main_loop_cond; - GMutex *main_loop_mutex; + GCond main_loop_cond; + GMutex main_loop_mutex; + GError *error; }; enum { @@ -216,6 +225,37 @@ auth_context_free (AuthContext *auth_context) g_slice_free (AuthContext, auth_context); } +static CreateContext * +create_context_new (void) +{ + CreateContext *create_context; + + create_context = g_slice_new0 (CreateContext); + + create_context->pending_uids = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + + create_context->main_context = g_main_context_new (); + + create_context->main_loop = g_main_loop_new ( + create_context->main_context, FALSE); + + return create_context; +} + +static void +create_context_free (CreateContext *create_context) +{ + g_main_loop_unref (create_context->main_loop); + g_main_context_unref (create_context->main_context); + g_hash_table_unref (create_context->pending_uids); + + g_slice_free (CreateContext, create_context); +} + static void source_closure_free (SourceClosure *closure) { @@ -232,8 +272,12 @@ thread_closure_free (ThreadClosure *closure) g_main_context_unref (closure->main_context); g_main_loop_unref (closure->main_loop); - g_cond_free (closure->main_loop_cond); - g_mutex_free (closure->main_loop_mutex); + g_cond_clear (&closure->main_loop_cond); + g_mutex_clear (&closure->main_loop_mutex); + + /* The GError should be NULL at this point, + * regardless of whether an error occurred. */ + g_warn_if_fail (closure->error == NULL); g_slice_free (ThreadClosure, closure); } @@ -246,14 +290,14 @@ source_registry_object_path_table_insert (ESourceRegistry *registry, g_return_if_fail (object_path != NULL); g_return_if_fail (E_IS_SOURCE (source)); - g_mutex_lock (registry->priv->object_path_table_lock); + g_mutex_lock (®istry->priv->object_path_table_lock); g_hash_table_insert ( registry->priv->object_path_table, g_strdup (object_path), g_object_ref (source)); - g_mutex_unlock (registry->priv->object_path_table_lock); + g_mutex_unlock (®istry->priv->object_path_table_lock); } static ESource * @@ -264,14 +308,14 @@ source_registry_object_path_table_lookup (ESourceRegistry *registry, g_return_val_if_fail (object_path != NULL, NULL); - g_mutex_lock (registry->priv->object_path_table_lock); + g_mutex_lock (®istry->priv->object_path_table_lock); source = g_hash_table_lookup ( registry->priv->object_path_table, object_path); if (source != NULL) g_object_ref (source); - g_mutex_unlock (registry->priv->object_path_table_lock); + g_mutex_unlock (®istry->priv->object_path_table_lock); return source; } @@ -284,12 +328,12 @@ source_registry_object_path_table_remove (ESourceRegistry *registry, g_return_val_if_fail (object_path != NULL, FALSE); - g_mutex_lock (registry->priv->object_path_table_lock); + g_mutex_lock (®istry->priv->object_path_table_lock); removed = g_hash_table_remove ( registry->priv->object_path_table, object_path); - g_mutex_unlock (registry->priv->object_path_table_lock); + g_mutex_unlock (®istry->priv->object_path_table_lock); return removed; } @@ -303,13 +347,13 @@ source_registry_sources_insert (ESourceRegistry *registry, uid = e_source_get_uid (source); g_return_if_fail (uid != NULL); - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); g_hash_table_insert ( registry->priv->sources, g_strdup (uid), g_object_ref (source)); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); } static gboolean @@ -322,11 +366,11 @@ source_registry_sources_remove (ESourceRegistry *registry, uid = e_source_get_uid (source); g_return_val_if_fail (uid != NULL, FALSE); - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); removed = g_hash_table_remove (registry->priv->sources, uid); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); return removed; } @@ -339,14 +383,14 @@ source_registry_sources_lookup (ESourceRegistry *registry, g_return_val_if_fail (uid != NULL, NULL); - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); source = g_hash_table_lookup (registry->priv->sources, uid); if (source != NULL) g_object_ref (source); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); return source; } @@ -356,13 +400,13 @@ source_registry_sources_get_values (ESourceRegistry *registry) { GList *values; - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); values = g_hash_table_get_values (registry->priv->sources); g_list_foreach (values, (GFunc) g_object_ref, NULL); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); return values; } @@ -375,7 +419,7 @@ source_registry_sources_build_tree (ESourceRegistry *registry) GHashTableIter iter; gpointer key, value; - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); root = g_node_new (NULL); index = g_hash_table_new (g_str_hash, g_str_equal); @@ -413,7 +457,7 @@ source_registry_sources_build_tree (ESourceRegistry *registry) g_hash_table_destroy (index); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); return root; } @@ -558,14 +602,17 @@ source_registry_add_source (ESourceRegistry *registry, { const gchar *uid; + /* This is called in the manager thread during initialization + * and in response to "object-added" signals from the manager. */ + uid = e_source_get_uid (source); g_return_if_fail (uid != NULL); - g_mutex_lock (registry->priv->sources_lock); + g_mutex_lock (®istry->priv->sources_lock); /* Check if we already have this source in the registry. */ if (g_hash_table_lookup (registry->priv->sources, uid) != NULL) { - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); return; } @@ -579,31 +626,19 @@ source_registry_add_source (ESourceRegistry *registry, G_CALLBACK (source_registry_source_notify_enabled_cb), registry); - g_mutex_unlock (registry->priv->sources_lock); + g_mutex_unlock (®istry->priv->sources_lock); source_registry_sources_insert (registry, source); - - g_signal_emit (registry, signals[SOURCE_ADDED], 0, source); -} - -static void -source_registry_remove_source (ESourceRegistry *registry, - ESource *source) -{ - g_object_ref (source); - - if (source_registry_sources_remove (registry, source)) - g_signal_emit (registry, signals[SOURCE_REMOVED], 0, source); - - g_object_unref (source); } static gboolean source_registry_object_added_idle_cb (gpointer user_data) { SourceClosure *closure = user_data; + ESourceRegistry *registry = closure->registry; + ESource *source = closure->source; - source_registry_add_source (closure->registry, closure->source); + g_signal_emit (registry, signals[SOURCE_ADDED], 0, source); return FALSE; } @@ -622,6 +657,10 @@ source_registry_object_added_cb (GDBusObjectManager *object_manager, source = source_registry_new_source (registry, dbus_object); g_return_if_fail (source != NULL); + /* Add the new ESource to our internal hash table so it can be + * obtained through e_source_registry_ref_source() immediately. */ + source_registry_add_source (registry, source); + /* Schedule a callback on the ESourceRegistry's GMainContext. */ closure = g_slice_new0 (SourceClosure); @@ -643,8 +682,13 @@ static gboolean source_registry_object_removed_idle_cb (gpointer user_data) { SourceClosure *closure = user_data; + ESourceRegistry *registry = closure->registry; + ESource *source = closure->source; - source_registry_remove_source (closure->registry, closure->source); + /* Removing the ESource won't finalize it because the + * SourceClosure itself still holds a reference on it. */ + if (source_registry_sources_remove (registry, source)) + g_signal_emit (registry, signals[SOURCE_REMOVED], 0, source); return FALSE; } @@ -691,9 +735,9 @@ source_registry_object_manager_running (gpointer data) { ThreadClosure *closure = data; - g_mutex_lock (closure->main_loop_mutex); - g_cond_broadcast (closure->main_loop_cond); - g_mutex_unlock (closure->main_loop_mutex); + g_mutex_lock (&closure->main_loop_mutex); + g_cond_broadcast (&closure->main_loop_cond); + g_mutex_unlock (&closure->main_loop_mutex); return FALSE; } @@ -705,9 +749,8 @@ source_registry_object_manager_thread (gpointer data) ThreadClosure *closure = data; GSource *idle_source; GList *list, *link; - gulong object_added_id; - gulong object_removed_id; - GError *error = NULL; + gulong object_added_id = 0; + gulong object_removed_id = 0; /* GDBusObjectManagerClient grabs the thread-default GMainContext * at creation time and only emits signals from that GMainContext. @@ -725,15 +768,19 @@ source_registry_object_manager_thread (gpointer data) G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, SOURCES_DBUS_SERVICE_NAME, DBUS_OBJECT_PATH, - NULL, &error); + NULL, &closure->error); - /* If this fails there's really no point in continuing - * since we rely on the object manager to populate the - * registry. Abort the process with a fatal error. */ - if (error != NULL) { - g_error ("%s", error->message); - g_assert_not_reached (); - } + /* Sanity check. */ + g_warn_if_fail ( + ((object_manager != NULL) && (closure->error == NULL)) || + ((object_manager == NULL) && (closure->error != NULL))); + + /* If we failed to create the GDBusObjectManagerClient, skip + * straight to the main loop. The GError will be propagated + * back to the caller, the main loop will terminate, and the + * partially-initialized ESourceRegistry will be destroyed. */ + if (object_manager == NULL) + goto notify; /* Give the registry a handle to the object manager. */ closure->registry->priv->dbus_object_manager = @@ -761,17 +808,6 @@ source_registry_object_manager_thread (gpointer data) g_list_free_full (list, (GDestroyNotify) g_object_unref); - /* Schedule a one-time idle callback to broadcast through a - * condition variable that our main loop is up and running. */ - - idle_source = g_idle_source_new (); - g_source_set_callback ( - idle_source, - source_registry_object_manager_running, - closure, (GDestroyNotify) NULL); - g_source_attach (idle_source, closure->main_context); - g_source_unref (idle_source); - /* Listen for D-Bus object additions and removals. */ object_added_id = g_signal_connect ( @@ -784,16 +820,29 @@ source_registry_object_manager_thread (gpointer data) G_CALLBACK (source_registry_object_removed_cb), closure->registry); +notify: + /* Schedule a one-time idle callback to broadcast through a + * condition variable that our main loop is up and running. */ + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_object_manager_running, + closure, (GDestroyNotify) NULL); + g_source_attach (idle_source, closure->main_context); + g_source_unref (idle_source); + /* Now we mostly idle here for the rest of the session. */ g_main_loop_run (closure->main_loop); /* Clean up and exit. */ - g_signal_handler_disconnect (object_manager, object_added_id); - g_signal_handler_disconnect (object_manager, object_removed_id); - - g_object_unref (object_manager); + if (object_manager != NULL) { + g_signal_handler_disconnect (object_manager, object_added_id); + g_signal_handler_disconnect (object_manager, object_removed_id); + g_object_unref (object_manager); + } g_main_context_pop_thread_default (closure->main_context); @@ -936,6 +985,7 @@ source_registry_dispose (GObject *object) g_hash_table_remove_all (priv->sources); if (priv->settings != NULL) { + g_signal_handlers_disconnect_by_data (priv->settings, object); g_object_unref (priv->settings); priv->settings = NULL; } @@ -952,10 +1002,10 @@ source_registry_finalize (GObject *object) priv = E_SOURCE_REGISTRY_GET_PRIVATE (object); g_hash_table_destroy (priv->object_path_table); - g_mutex_free (priv->object_path_table_lock); + g_mutex_clear (&priv->object_path_table_lock); g_hash_table_destroy (priv->sources); - g_mutex_free (priv->sources_lock); + g_mutex_clear (&priv->sources_lock); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_source_registry_parent_class)->finalize (object); @@ -978,34 +1028,53 @@ source_registry_initable_init (GInitable *initable, * we wait for the main loop to start running as a way of * synchronizing with the manager thread. */ closure->main_loop = g_main_loop_new (closure->main_context, FALSE); - closure->main_loop_cond = g_cond_new (); - closure->main_loop_mutex = g_mutex_new (); + g_cond_init (&closure->main_loop_cond); + g_mutex_init (&closure->main_loop_mutex); registry->priv->thread_closure = closure; - registry->priv->manager_thread = g_thread_create ( + registry->priv->manager_thread = g_thread_new ( + NULL, source_registry_object_manager_thread, - closure, TRUE /* joinable */, error); + closure); if (registry->priv->manager_thread == NULL) return FALSE; /* Wait for notification that the manager * thread's main loop has been started. */ - g_mutex_lock (closure->main_loop_mutex); + g_mutex_lock (&closure->main_loop_mutex); while (!g_main_loop_is_running (closure->main_loop)) g_cond_wait ( - closure->main_loop_cond, - closure->main_loop_mutex); - g_mutex_unlock (closure->main_loop_mutex); - - /* We should now have a GDBusObjectManagerClient available. */ - g_return_val_if_fail ( - G_IS_DBUS_OBJECT_MANAGER_CLIENT ( - registry->priv->dbus_object_manager), FALSE); + &closure->main_loop_cond, + &closure->main_loop_mutex); + g_mutex_unlock (&closure->main_loop_mutex); + + /* Check for error in the manager thread. */ + if (closure->error != NULL) { + g_propagate_error (error, closure->error); + closure->error = NULL; + return FALSE; + } - /* The registry should now be populated with sources. */ - g_warn_if_fail (g_hash_table_size (registry->priv->sources) > 0); + /* The registry should now be populated with sources. + * + * XXX Actually, not necessarily if the registry service was + * just now activated. There may yet be a small window + * while the registry service starts up before it exports + * any sources, even built-in sources. This COULD create + * problems if any logic that depends on those built-in + * sources executes during this time window, but so far + * we haven't seen any cases of that. + * + * Attempts in the past to stop and wait for sources to + * show up have proven problematic. See for example: + * https://bugzilla.gnome.org/678378 + * + * Leave the runtime check disabled for the moment. + * I have a feeling I'll be revisiting this again. + */ + /*g_warn_if_fail (g_hash_table_size (registry->priv->sources) > 0);*/ /* The EDBusSourceManagerProxy is just another D-Bus interface * that resides at the same object path. It's unrelated to the @@ -1021,6 +1090,14 @@ source_registry_initable_init (GInitable *initable, if (registry->priv->dbus_source_manager == NULL) return FALSE; + /* Allow authentication prompts for all exported data sources + * when a new EDBusSourceManagerProxy is created. The thought + * being, if you cancel an authentication prompt you will not + * be bothered again until you start (or restart) a new E-D-S + * client app. Failure here is non-fatal, ignore errors. */ + e_dbus_source_manager_call_allow_auth_prompt_all_sync ( + registry->priv->dbus_source_manager, cancellable, NULL); + return TRUE; } @@ -1246,7 +1323,7 @@ e_source_registry_init (ESourceRegistry *registry) (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); - registry->priv->object_path_table_lock = g_mutex_new (); + g_mutex_init (®istry->priv->object_path_table_lock); /* UID string -> ESource */ registry->priv->sources = g_hash_table_new_full ( @@ -1255,7 +1332,7 @@ e_source_registry_init (ESourceRegistry *registry) (GDestroyNotify) g_free, (GDestroyNotify) source_registry_unref_source); - registry->priv->sources_lock = g_mutex_new (); + g_mutex_init (®istry->priv->sources_lock); registry->priv->settings = g_settings_new (GSETTINGS_SCHEMA); @@ -1281,6 +1358,11 @@ ESourceRegistry * e_source_registry_new_sync (GCancellable *cancellable, GError **error) { + /* XXX Work around http://bugzilla.gnome.org/show_bug.cgi?id=683519 + * until GObject's type initialization deadlock issue is fixed. + * Apparently only the synchronous instantiation is affected. */ + g_type_ensure (G_TYPE_DBUS_CONNECTION); + return g_initable_new ( E_TYPE_SOURCE_REGISTRY, cancellable, error, NULL); @@ -1386,9 +1468,25 @@ source_registry_authenticate_respond_cb (AuthContext *auth_context) * session will either time out on its own or the authentication * dialog will eventually be dismissed by the user. */ + /* If we were cancelled from our side, we have a bit of a dilemma. + * We need to tell the server to cancel the authentication session, + * but that involves making a synchronous D-Bus call, which we are + * not supposed to do if we know we've been cancelled. But if we + * don't tell the server, the authentication session will be left + * to timeout on its own (which may take minutes), and meanwhile + * all other authentication requests are blocked. So choose the + * lesser evil and make the synchronous call but without passing + * the already-cancelled GCancellable. */ + if (g_cancellable_is_cancelled (auth_context->cancellable)) { + e_dbus_authenticator_call_cancel_sync ( + auth_context->dbus_auth, + NULL, &non_fatal_error); + g_main_loop_quit (auth_context->main_loop); + auth_context->success = FALSE; + /* If an error occurred while attempting to authenticate, * tell the server to cancel the authentication session. */ - if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) { + } else if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) { e_dbus_authenticator_call_cancel_sync ( auth_context->dbus_auth, auth_context->cancellable, @@ -1674,6 +1772,12 @@ e_source_registry_authenticate_sync (ESourceRegistry *registry, exit: g_main_context_pop_thread_default (main_context); + + /* Make sure the main_context doesn't have pending operations; + workarounds https://bugzilla.gnome.org/show_bug.cgi?id=690126 */ + while (g_main_context_pending (main_context)) + g_main_context_iteration (main_context, FALSE); + g_main_context_unref (main_context); return success; @@ -1808,7 +1912,9 @@ source_registry_commit_source_thread (GSimpleAsyncResult *simple, * * If @source does NOT have a #GDBusObject (implying it's a scratch * #ESource), its contents are submitted to the D-Bus service through - * e_source_registry_create_sources_sync(). + * either e_source_remote_create_sync() if @source is to be a collection + * member, or e_source_registry_create_sources_sync() if @source to be an + * independent data source. * * If an error occurs, the function will set @error and return %FALSE. * @@ -1823,6 +1929,8 @@ e_source_registry_commit_source_sync (ESourceRegistry *registry, GError **error) { GDBusObject *dbus_object; + ESource *collection_source; + gboolean collection_member; gboolean success; g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); @@ -1830,9 +1938,21 @@ e_source_registry_commit_source_sync (ESourceRegistry *registry, dbus_object = e_source_ref_dbus_object (source); + collection_source = e_source_registry_find_extension ( + registry, source, E_SOURCE_EXTENSION_COLLECTION); + + collection_member = + (collection_source != NULL) && + (collection_source != source); + if (dbus_object != NULL) { success = e_source_write_sync (source, cancellable, error); g_object_unref (dbus_object); + + } else if (collection_member) { + success = e_source_remote_create_sync ( + collection_source, source, cancellable, error); + } else { GList *list = g_list_prepend (NULL, source); success = e_source_registry_create_sources_sync ( @@ -1840,6 +1960,9 @@ e_source_registry_commit_source_sync (ESourceRegistry *registry, g_list_free (list); } + if (collection_source != NULL) + g_object_unref (collection_source); + return success; } @@ -1944,6 +2067,49 @@ source_registry_create_sources_thread (GSimpleAsyncResult *simple, g_simple_async_result_take_error (simple, error); } +/* Helper for e_source_registry_create_sources_sync() */ +static gboolean +source_registry_create_sources_main_loop_quit_cb (gpointer user_data) +{ + GMainLoop *main_loop = user_data; + + g_main_loop_quit (main_loop); + + return FALSE; +} + +/* Helper for e_source_registry_create_sources_sync() */ +static void +source_registry_create_sources_object_added_cb (GDBusObjectManager *object_manager, + GDBusObject *dbus_object, + CreateContext *create_context) +{ + EDBusObject *e_dbus_object; + EDBusSource *e_dbus_source; + const gchar *uid; + + e_dbus_object = E_DBUS_OBJECT (dbus_object); + e_dbus_source = e_dbus_object_get_source (e_dbus_object); + uid = e_dbus_source_get_uid (e_dbus_source); + + g_hash_table_remove (create_context->pending_uids, uid); + + /* The hash table will be empty when all of the expected + * GDBusObjects have been added to the GDBusObjectManager. */ + if (g_hash_table_size (create_context->pending_uids) == 0) { + GSource *idle_source; + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_create_sources_main_loop_quit_cb, + g_main_loop_ref (create_context->main_loop), + (GDestroyNotify) g_main_loop_unref); + g_source_attach (idle_source, create_context->main_context); + g_source_unref (idle_source); + } +} + /** * e_source_registry_create_sources_sync: * @registry: an #ESourceRegistry @@ -1968,9 +2134,11 @@ e_source_registry_create_sources_sync (ESourceRegistry *registry, GCancellable *cancellable, GError **error) { + CreateContext *create_context; GVariantBuilder builder; GVariant *variant; GList *link; + gulong object_added_id; gboolean success; g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); @@ -1979,15 +2147,21 @@ e_source_registry_create_sources_sync (ESourceRegistry *registry, for (link = list_of_sources; link != NULL; link = g_list_next (link)) g_return_val_if_fail (E_IS_SOURCE (link->data), FALSE); + create_context = create_context_new (); + g_main_context_push_thread_default (create_context->main_context); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); for (link = list_of_sources; link != NULL; link = g_list_next (link)) { ESource *source; - const gchar *uid; gchar *source_data; + gchar *uid; source = E_SOURCE (link->data); - uid = e_source_get_uid (source); + uid = e_source_dup_uid (source); + + /* Takes ownership of the UID string. */ + g_hash_table_add (create_context->pending_uids, uid); source_data = e_source_to_string (source, NULL); g_variant_builder_add (&builder, "{ss}", uid, source_data); @@ -1996,6 +2170,14 @@ e_source_registry_create_sources_sync (ESourceRegistry *registry, variant = g_variant_builder_end (&builder); + /* Use G_CONNECT_AFTER so source_registry_object_added_cb() + * runs first and actually adds the ESource to the internal + * hash table before we go quitting our main loop. */ + object_added_id = g_signal_connect_after ( + registry->priv->dbus_object_manager, "object-added", + G_CALLBACK (source_registry_create_sources_object_added_cb), + create_context); + /* This function sinks the floating GVariant reference. */ success = e_dbus_source_manager_call_create_sources_sync ( registry->priv->dbus_source_manager, @@ -2003,6 +2185,31 @@ e_source_registry_create_sources_sync (ESourceRegistry *registry, g_variant_builder_clear (&builder); + /* Wait for an "object-added" signal for each created ESource. + * But also set a short timeout to avoid getting stuck here in + * case the registry service adds sources to its orphan table, + * which prevents them from being exported over D-Bus. */ + if (success) { + GSource *timeout_source; + + timeout_source = g_timeout_source_new_seconds (2); + g_source_set_callback ( + timeout_source, + source_registry_create_sources_main_loop_quit_cb, + g_main_loop_ref (create_context->main_loop), + (GDestroyNotify) g_main_loop_unref); + g_source_attach (timeout_source, create_context->main_context); + g_source_unref (timeout_source); + + g_main_loop_run (create_context->main_loop); + } + + g_signal_handler_disconnect ( + registry->priv->dbus_object_manager, object_added_id); + + g_main_context_pop_thread_default (create_context->main_context); + create_context_free (create_context); + return success; } @@ -2178,6 +2385,58 @@ e_source_registry_list_sources (ESourceRegistry *registry, } /** + * e_source_registry_check_enabled: + * @registry: an #ESourceRegistry + * @source: an #ESource + * + * Determines whether @source is "effectively" enabled by examining its + * own #ESource:enabled property as well as those of its ancestors in the + * #ESource hierarchy. If all examined #ESource:enabled properties are + * %TRUE, then the function returns %TRUE. If any are %FALSE, then the + * function returns %FALSE. + * + * Use this function instead of e_source_get_enabled() to determine + * things like whether to display an #ESource in a user interface or + * whether to act on the data set described by the #ESource. + * + * Returns: whether @source is "effectively" enabled + * + * Since: 3.8 + **/ +gboolean +e_source_registry_check_enabled (ESourceRegistry *registry, + ESource *source) +{ + gboolean enabled; + gchar *parent_uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + enabled = e_source_get_enabled (source); + parent_uid = e_source_dup_parent (source); + + while (enabled && parent_uid != NULL) { + ESource *parent; + + parent = e_source_registry_ref_source (registry, parent_uid); + + g_free (parent_uid); + parent_uid = NULL; + + if (parent != NULL) { + enabled = e_source_get_enabled (parent); + parent_uid = e_source_dup_parent (parent); + g_object_unref (parent); + } + } + + g_free (parent_uid); + + return enabled; +} + +/** * e_source_registry_find_extension: * @registry: an #ESourceRegistry * @source: an #ESource @@ -2484,7 +2743,6 @@ e_source_registry_ref_builtin_address_book (ESourceRegistry *registry) uid = E_SOURCE_BUILTIN_ADDRESS_BOOK_UID; source = e_source_registry_ref_source (registry, uid); - g_return_val_if_fail (source != NULL, NULL); return source; } @@ -2518,12 +2776,10 @@ e_source_registry_ref_default_address_book (ESourceRegistry *registry) source = e_source_registry_ref_source (registry, uid); g_free (uid); - /* The built-in source is always present. */ + /* The built-in source is present in normal EDS installations. */ if (source == NULL) source = e_source_registry_ref_builtin_address_book (registry); - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - return source; } @@ -2586,7 +2842,6 @@ e_source_registry_ref_builtin_calendar (ESourceRegistry *registry) uid = E_SOURCE_BUILTIN_CALENDAR_UID; source = e_source_registry_ref_source (registry, uid); - g_return_val_if_fail (source != NULL, NULL); return source; } @@ -2620,12 +2875,10 @@ e_source_registry_ref_default_calendar (ESourceRegistry *registry) source = e_source_registry_ref_source (registry, uid); g_free (uid); - /* The built-in source is always present. */ + /* The built-in source is present in normal EDS installations. */ if (source == NULL) source = e_source_registry_ref_builtin_calendar (registry); - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - return source; } @@ -2808,14 +3061,14 @@ source_registry_ref_any_mail_identity (ESourceRegistry *registry) for (link = list; link != NULL; link = g_list_next (link)) { ESource *candidate = E_SOURCE (link->data); - if (e_source_get_enabled (candidate)) { + if (e_source_registry_check_enabled (registry, candidate)) { source = g_object_ref (candidate); break; } } if (source == NULL && list != NULL) - source = g_object_ref (link->data); + source = g_object_ref (list->data); g_list_free_full (list, (GDestroyNotify) g_object_unref);