From e4c54d4c213316bddc3c39c6e902ee8f91ad47ce Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Wed, 3 Apr 2019 17:51:31 +0000 Subject: [PATCH 1/3] Silence some warnings --- src/cloudprovidersaccountexporter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cloudprovidersaccountexporter.c b/src/cloudprovidersaccountexporter.c index cf6a281..2f22774 100644 --- a/src/cloudprovidersaccountexporter.c +++ b/src/cloudprovidersaccountexporter.c @@ -416,7 +416,6 @@ static void cloud_providers_account_exporter_constructed (GObject *object) { const gchar *provider_object_path; - g_autofree gchar *object_path = NULL; CloudProvidersAccountExporter *self = CLOUD_PROVIDERS_ACCOUNT_EXPORTER (object); self->bus = cloud_providers_provider_exporter_get_bus (self->provider); -- GitLab From 18e814ddcb24b0fd875cdbc1bed9ad959afa316c Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Wed, 3 Apr 2019 19:27:41 +0000 Subject: [PATCH 2/3] Load cloud providers from XDG desktop files Closes #7. This will allow libcloudprovider to work better with Flatpaks. --- meson_options.txt | 2 +- src/cloudproviderscollector.c | 211 ++++++++++++++---- test/meson.build | 2 + ...sktop.CloudProviders.ServerExample.desktop | 5 + 4 files changed, 174 insertions(+), 46 deletions(-) create mode 100644 test/org.freedesktop.CloudProviders.ServerExample.desktop diff --git a/meson_options.txt b/meson_options.txt index 3fa709e..d855bb2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,4 +11,4 @@ option( type: 'boolean', value: true, description: 'Build Vala bindings', -) \ No newline at end of file +) diff --git a/src/cloudproviderscollector.c b/src/cloudproviderscollector.c index 7de5efc..252ad63 100644 --- a/src/cloudproviderscollector.c +++ b/src/cloudproviderscollector.c @@ -26,6 +26,7 @@ #include #define KEY_FILE_GROUP "Cloud Providers" +#define XDG_DESKTOP_IMPLEMENTS "org.freedesktop.CloudProviders" struct _CloudProvidersCollector { @@ -155,8 +156,120 @@ on_provider_removed (CloudProvidersCollector *self) } static void -load_cloud_provider (CloudProvidersCollector *self, - GFile *file) +add_cloud_provider (CloudProvidersCollector *self, + const gchar *bus_name, + const gchar *object_path) +{ + CloudProvidersProvider *provider; + + provider = cloud_providers_provider_new (bus_name, object_path); + self->providers = g_list_append (self->providers, provider); + g_signal_connect_swapped (provider, "removed", + G_CALLBACK (on_provider_removed), self); + + g_debug ("Client loading provider: %s %s\n", bus_name, object_path); +} + +static void +load_cloud_provider_from_desktop_file (CloudProvidersCollector *self, + GFile *file) +{ + GKeyFile *key_file; + GError *error = NULL; + gchar *path = NULL; + gchar **implements = NULL; + gchar *bus_name = NULL; + gchar *object_path = NULL; + gsize i; + gsize n_implements; + gboolean found_provider = FALSE; + + path = g_file_get_path (file); + if (!g_str_has_suffix (path, ".desktop")) + { + g_free (path); + return; + } + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error)) + { + g_warning ("Error loading .desktop file at %s: %s", path, error->message); + goto out; + } + + implements = g_key_file_get_string_list (key_file, "Desktop Entry", "Implements", &n_implements, &error); + if (implements == NULL) + { + if (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) + { + g_warning ("Error loading Implements key from %s: %s", path, error->message); + goto out; + } + } + + for (i = 0; i < n_implements; i++) + { + if (strcmp (implements[i], XDG_DESKTOP_IMPLEMENTS) == 0) + { + found_provider = TRUE;; + break; + } + } + + if (!found_provider) + { + goto out; + } + + bus_name = g_key_file_get_string (key_file, XDG_DESKTOP_IMPLEMENTS, "BusName", NULL); + object_path = g_key_file_get_string (key_file, XDG_DESKTOP_IMPLEMENTS, "ObjectPath", NULL); + + if (bus_name == NULL) + { + /* The default bus name is the .desktop file without the extension, just like XDG desktop D-Bus activation */ + gchar *extension; + bus_name = g_file_get_basename (file); + extension = strrchr (bus_name, '.'); + *extension = '\0'; + } + + if (!g_dbus_is_name (bus_name) || g_dbus_is_unique_name (bus_name)) + { + g_warning ("Invalid bus name: %s", bus_name); + goto out; + } + + if (object_path == NULL) + { + /* Convert the bus name into an object path */ + gchar *ptr; + object_path = g_strdup_printf ("/%s", bus_name); + for (ptr = object_path; ptr != NULL; ptr = strchr (ptr, '.')) + { + *ptr = '/'; + } + } + + if (!g_variant_is_object_path (object_path)) + { + g_warning ("Invalid object path: %s", object_path); + goto out; + } + + add_cloud_provider (self, bus_name, object_path); +out: + g_free (bus_name); + g_free (object_path); + g_strfreev (implements); + g_key_file_free (key_file); + g_free (path); + g_clear_error (&error); +} + +static void +load_cloud_provider_from_key_file (CloudProvidersCollector *self, + GFile *file) { GKeyFile *key_file; gchar *path; @@ -164,7 +277,6 @@ load_cloud_provider (CloudProvidersCollector *self, gchar *bus_name; gchar *object_path; gboolean success = FALSE; - CloudProvidersProvider *provider; key_file = g_key_file_new (); path = g_file_get_path (file); @@ -194,12 +306,7 @@ load_cloud_provider (CloudProvidersCollector *self, goto out; } - provider = cloud_providers_provider_new (bus_name, object_path); - self->providers = g_list_append (self->providers, provider); - g_signal_connect_swapped (provider, "removed", - G_CALLBACK (on_provider_removed), self); - - g_debug("Client loading provider: %s %s\n", bus_name, object_path); + add_cloud_provider (self, bus_name, object_path); success = TRUE; g_free (bus_name); @@ -210,7 +317,6 @@ out: g_warning ("Error while loading cloud provider key file at %s with error %s", path, error != NULL ? error->message : NULL); } g_key_file_free (key_file); - g_object_unref (file); g_free (path); } @@ -225,52 +331,67 @@ load_cloud_providers (CloudProvidersCollector *self) { const gchar* const *data_dirs; gint i; + gint j; gint len; - gchar *key_files_directory_path; - GFile *key_files_directory_file; GError *error = NULL; - GFileEnumerator *file_enumerator; + + const gchar *data_subdirs[] = {"applications", "cloud-providers"}; data_dirs = g_get_system_data_dirs (); len = g_strv_length ((gchar **)data_dirs); for (i = 0; i < len; i++) { - GFileInfo *info; - GFileMonitor *monitor; - - key_files_directory_path = g_build_filename (data_dirs[i], "cloud-providers", NULL); - key_files_directory_file = g_file_new_for_path (key_files_directory_path); - monitor = g_file_monitor (key_files_directory_file, G_FILE_MONITOR_WATCH_MOVES, - self->cancellable, NULL); - g_signal_connect_swapped (monitor, "changed", G_CALLBACK (on_providers_file_changed), self); - self->monitors = g_list_append (self->monitors, monitor); - file_enumerator = g_file_enumerate_children (key_files_directory_file, - "standard::name,standard::type", - G_FILE_QUERY_INFO_NONE, - NULL, - &error); - if (error) + for (j = 0; j < G_N_ELEMENTS (data_subdirs); j++) { - error = NULL; - continue; - } + gchar *directory_path; + GFile *directory_file; + GFileInfo *info; + GFileMonitor *monitor; + GFileEnumerator *file_enumerator; + + directory_path = g_build_filename (data_dirs[i], data_subdirs[j], NULL); + directory_file = g_file_new_for_path (directory_path); + monitor = g_file_monitor (directory_file, G_FILE_MONITOR_WATCH_MOVES, + self->cancellable, NULL); + g_signal_connect_swapped (monitor, "changed", G_CALLBACK (on_providers_file_changed), self); + self->monitors = g_list_append (self->monitors, monitor); + file_enumerator = g_file_enumerate_children (directory_file, + "standard::name,standard::type", + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (error) + { + error = NULL; + continue; + } - info = g_file_enumerator_next_file (file_enumerator, NULL, &error); - if (error) - { - g_warning ("Error while enumerating file %s error: %s\n", key_files_directory_path, error->message); - error = NULL; - continue; - } - while (info != NULL && error == NULL) - { - load_cloud_provider (self, g_file_enumerator_get_child (file_enumerator, info)); - g_object_unref (info); info = g_file_enumerator_next_file (file_enumerator, NULL, &error); + if (error) + { + g_warning ("Error while enumerating file %s error: %s\n", directory_path, error->message); + error = NULL; + continue; + } + while (info != NULL && error == NULL) + { + GFile *child = g_file_enumerator_get_child (file_enumerator, info); + if (strcmp (data_subdirs[j], "applications") == 0) + { + load_cloud_provider_from_desktop_file (self, child); + } + else + { + load_cloud_provider_from_key_file (self, child); + } + g_object_unref (child); + g_object_unref (info); + info = g_file_enumerator_next_file (file_enumerator, NULL, &error); + } + g_object_unref (file_enumerator); + g_free (directory_path); + g_object_unref (directory_file); } - g_object_unref (file_enumerator); - g_free (key_files_directory_path); - g_object_unref (key_files_directory_file); } } diff --git a/test/meson.build b/test/meson.build index 30379b2..fd3c655 100644 --- a/test/meson.build +++ b/test/meson.build @@ -21,6 +21,8 @@ if installed configuration: service_conf, install_dir: servicedir, ) + applications = join_paths (datadir, 'applications') cloudprovidersdata = join_paths (datadir, 'cloud-providers') + install_data('org.freedesktop.CloudProviders.ServerExample.desktop', install_dir: applications) install_data('org.freedesktop.CloudProviders.ServerExample.ini', install_dir: cloudprovidersdata) endif diff --git a/test/org.freedesktop.CloudProviders.ServerExample.desktop b/test/org.freedesktop.CloudProviders.ServerExample.desktop new file mode 100644 index 0000000..e8a5e5b --- /dev/null +++ b/test/org.freedesktop.CloudProviders.ServerExample.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Application +Name=libcloudproviders example server +NoDisplay=true +Implements=org.freedesktop.CloudProviders -- GitLab From c34db38cd7bfb615ba73ec4056b0fc84fb5ed871 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Wed, 3 Apr 2019 19:49:32 +0000 Subject: [PATCH 3/3] Avoid adding duplicate cloud providers This is now possible because of loading both desktop files and ini files. --- src/cloudproviderscollector.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cloudproviderscollector.c b/src/cloudproviderscollector.c index 252ad63..27a37c9 100644 --- a/src/cloudproviderscollector.c +++ b/src/cloudproviderscollector.c @@ -33,6 +33,7 @@ struct _CloudProvidersCollector GObject parent; GList *providers; + GHashTable *providers_bus_names; GHashTable* provider_object_managers; GDBusConnection *bus; GCancellable *cancellable; @@ -104,6 +105,7 @@ cloud_providers_collector_finalize (GObject *object) { g_signal_handlers_disconnect_by_data (G_OBJECT (l->data), self); } + g_hash_table_unref (self->providers_bus_names); g_list_free_full (self->providers, g_object_unref); g_list_free_full (self->monitors, g_object_unref); @@ -136,6 +138,7 @@ cloud_providers_collector_class_init (CloudProvidersCollectorClass *klass) static void cloud_providers_collector_init (CloudProvidersCollector *self) { + self->providers_bus_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } /** @@ -163,7 +166,15 @@ add_cloud_provider (CloudProvidersCollector *self, CloudProvidersProvider *provider; provider = cloud_providers_provider_new (bus_name, object_path); + if (g_hash_table_contains (self->providers_bus_names, bus_name)) + { + g_debug ("Skipped duplicate provider: %s %s\n", bus_name, object_path); + g_object_unref (provider); + return; + } + self->providers = g_list_append (self->providers, provider); + g_hash_table_add (self->providers_bus_names, g_strdup (bus_name)); g_signal_connect_swapped (provider, "removed", G_CALLBACK (on_provider_removed), self); @@ -410,6 +421,7 @@ update_cloud_providers (CloudProvidersCollector *self) { g_signal_handlers_disconnect_by_data (G_OBJECT (l->data), self); } + g_hash_table_remove_all (self->providers_bus_names); g_list_free_full (self->providers, g_object_unref); g_list_free_full (self->monitors, g_object_unref); self->providers = NULL; -- GitLab