From 764e187fd9f1f54a2c622fce2b3e1c0aa4fafdf0 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 26 Sep 2008 19:57:36 +0000 Subject: [PATCH] =?UTF-8?q?Bug=20545350=20=E2=80=93=20GAppInfo=20deletion?= =?UTF-8?q?=20Bug=20545351=20=E2=80=93=20Reset=20associations=20for?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2008-09-26 Matthias Clasen Bug 545350 – GAppInfo deletion Bug 545351 – Reset associations for content type * gio.symbols: * gappinfo.[hc]: New functions g_app_info_can_delete, g_app_info_delete and g_app_info_reset_type_associations. * gdesktopappinfo.c: * gwin32appinfo.c: Implementations of these. * tests/Makefile.am: * tests/desktop-app-info.c: Tests for GAppInfo functionality. svn path=/trunk/; revision=7554 --- gio/ChangeLog | 15 +++ gio/gappinfo.c | 57 ++++++++ gio/gappinfo.h | 7 + gio/gdesktopappinfo.c | 217 +++++++++++++++++++++--------- gio/gio.symbols | 3 + gio/gwin32appinfo.c | 6 + gio/tests/Makefile.am | 7 +- gio/tests/desktop-app-info.c | 253 +++++++++++++++++++++++++++++++++++ 8 files changed, 498 insertions(+), 67 deletions(-) create mode 100644 gio/tests/desktop-app-info.c diff --git a/gio/ChangeLog b/gio/ChangeLog index 2f6aa0642..e99b804ae 100644 --- a/gio/ChangeLog +++ b/gio/ChangeLog @@ -1,3 +1,18 @@ +2008-09-26 Matthias Clasen + + Bug 545350 – GAppInfo deletion + Bug 545351 – Reset associations for content type + + * gio.symbols: + * gappinfo.[hc]: New functions g_app_info_can_delete, + g_app_info_delete and g_app_info_reset_type_associations. + + * gdesktopappinfo.c: + * gwin32appinfo.c: Implementations of these. + + * tests/Makefile.am: + * tests/desktop-app-info.c: Tests for GAppInfo functionality. + 2008-09-26 Dan Winship Bug 505361 – gunixinputstream.c assumes poll() available diff --git a/gio/gappinfo.c b/gio/gappinfo.c index 5352aeabd..e32c9ba00 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -574,6 +574,62 @@ g_app_info_launch_default_for_uri (const char *uri, return res; } +/** + * g_app_info_can_delete: + * @appinfo: a #GAppInfo + * + * Obtains the information whether the GAppInfo can be deleted. + * See g_app_info_delete(). + * + * Returns: %TRUE if @appinfo can be deleted + * + * Since: 2.20 + */ +gboolean +g_app_info_can_delete (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->can_delete) + return (* iface->can_delete) (appinfo); + + return FALSE; +} + + +/** + * g_app_info_delete: + * @appinfo: a #GAppInfo + * + * Tries to delete an #GAppInfo. + * + * On some platforms, there may be a difference between user-defined + * #GAppInfos which can be deleted, and system-wide ones which + * cannot. See g_app_info_can_delete(). + * + * Returns: %TRUE if @appinfo has been deleted + * + * Since: 2.20 + */ +gboolean +g_app_info_delete (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->do_delete) + return (* iface->do_delete) (appinfo); + + return FALSE; +} + G_DEFINE_TYPE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT); @@ -690,5 +746,6 @@ g_app_launch_context_launch_failed (GAppLaunchContext *context, class->launch_failed (context, startup_notify_id); } + #define __G_APP_INFO_C__ #include "gioaliasdef.c" diff --git a/gio/gappinfo.h b/gio/gappinfo.h index 74464aa0d..14ac96a58 100644 --- a/gio/gappinfo.h +++ b/gio/gappinfo.h @@ -75,6 +75,8 @@ typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate; * @add_supports_type: Adds to the #GAppInfo information about supported file types. * @can_remove_supports_type: Checks for support for removing supported file types from a #GAppInfo. * @remove_supports_type: Removes a supported application type from a #GAppInfo. + * @can_delete: Checks if a #GAppInfo can be deleted. Since 2.20 + * @do_delete: Deletes a #GAppInfo. Since 2.20 * * Application Information interface, for operating system portability. */ @@ -120,6 +122,8 @@ struct _GAppInfoIface gboolean (* remove_supports_type) (GAppInfo *appinfo, const char *content_type, GError **error); + gboolean (* can_delete) (GAppInfo *appinfo); + gboolean (* do_delete) (GAppInfo *appinfo); }; GType g_app_info_get_type (void) G_GNUC_CONST; @@ -160,9 +164,12 @@ gboolean g_app_info_can_remove_supports_type (GAppInfo *appin gboolean g_app_info_remove_supports_type (GAppInfo *appinfo, const char *content_type, GError **error); +gboolean g_app_info_can_delete (GAppInfo *appinfo); +gboolean g_app_info_delete (GAppInfo *appinfo); GList * g_app_info_get_all (void); GList * g_app_info_get_all_for_type (const char *content_type); +void g_app_info_reset_type_associations (const char *content_type); GAppInfo *g_app_info_get_default_for_type (const char *content_type, gboolean must_support_uris); GAppInfo *g_app_info_get_default_for_uri_scheme (const char *uri_scheme); diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index b86e420ad..866bc3528 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -1171,7 +1171,8 @@ update_mimeapps_list (const char *desktop_id, char **list; gsize length, data_size; char *data; - int i, j; + int i, j, k; + char **content_types; /* Don't add both at start and end */ g_assert (!(add_at_start && add_at_end)); @@ -1191,75 +1192,109 @@ update_mimeapps_list (const char *desktop_id, key_file = g_key_file_new (); } - /* Add to the right place in the list */ - - length = 0; - old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP, - content_type, &length, NULL); - - list = g_new (char *, 1 + length + 1); - - i = 0; - if (add_at_start) - list[i++] = g_strdup (desktop_id); - if (old_list) + if (content_type) { - for (j = 0; old_list[j] != NULL; j++) - { - if (strcmp (old_list[j], desktop_id) != 0) - list[i++] = g_strdup (old_list[j]); - } + content_types = g_new (char *, 2); + content_types[0] = g_strdup (content_type); + content_types[1] = NULL; } - if (add_at_end) - list[i++] = g_strdup (desktop_id); - list[i] = NULL; - - g_strfreev (old_list); - - g_key_file_set_string_list (key_file, - ADDED_ASSOCIATIONS_GROUP, - content_type, - (const char * const *)list, i); - - g_strfreev (list); - - /* Remove from removed associations group (unless remove) */ - - length = 0; - old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP, - content_type, &length, NULL); - - list = g_new (char *, 1 + length + 1); - - i = 0; - if (remove) - list[i++] = g_strdup (desktop_id); - if (old_list) - { - for (j = 0; old_list[j] != NULL; j++) - { - if (strcmp (old_list[j], desktop_id) != 0) - list[i++] = g_strdup (old_list[j]); - } - } - list[i] = NULL; - - g_strfreev (old_list); - - if (list[0] == NULL) - g_key_file_remove_key (key_file, - REMOVED_ASSOCIATIONS_GROUP, - content_type, - NULL); else - g_key_file_set_string_list (key_file, - REMOVED_ASSOCIATIONS_GROUP, - content_type, - (const char * const *)list, i); - - g_strfreev (list); + { + content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL); + } + for (k = 0; content_types && content_types[k]; k++) + { + /* Add to the right place in the list */ + length = 0; + old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP, + content_types[k], &length, NULL); + + list = g_new (char *, 1 + length + 1); + + i = 0; + if (add_at_start) + list[i++] = g_strdup (desktop_id); + if (old_list) + { + for (j = 0; old_list[j] != NULL; j++) + { + if (g_strcmp0 (old_list[j], desktop_id) != 0) + list[i++] = g_strdup (old_list[j]); + } + } + if (add_at_end) + list[i++] = g_strdup (desktop_id); + list[i] = NULL; + + g_strfreev (old_list); + + if (list[0] == NULL || desktop_id == NULL) + g_key_file_remove_key (key_file, + ADDED_ASSOCIATIONS_GROUP, + content_types[k], + NULL); + else + g_key_file_set_string_list (key_file, + ADDED_ASSOCIATIONS_GROUP, + content_types[k], + (const char * const *)list, i); + + g_strfreev (list); + } + + if (content_type) + { + /* reuse the list from above */ + } + else + { + g_strfreev (content_types); + content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL); + } + + for (k = 0; content_types && content_types[k]; k++) + { + /* Remove from removed associations group (unless remove) */ + + length = 0; + old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP, + content_types[k], &length, NULL); + + list = g_new (char *, 1 + length + 1); + + i = 0; + if (remove) + list[i++] = g_strdup (desktop_id); + if (old_list) + { + for (j = 0; old_list[j] != NULL; j++) + { + if (g_strcmp0 (old_list[j], desktop_id) != 0) + list[i++] = g_strdup (old_list[j]); + } + } + list[i] = NULL; + + g_strfreev (old_list); + + if (list[0] == NULL || desktop_id == NULL) + g_key_file_remove_key (key_file, + REMOVED_ASSOCIATIONS_GROUP, + content_types[k], + NULL); + else + g_key_file_set_string_list (key_file, + REMOVED_ASSOCIATIONS_GROUP, + content_types[k], + (const char * const *)list, i); + + g_strfreev (list); + } + + g_strfreev (content_types); + data = g_key_file_to_data (key_file, &data_size, error); g_key_file_free (key_file); @@ -1513,6 +1548,40 @@ g_desktop_app_info_ensure_saved (GDesktopAppInfo *info, return TRUE; } +static gboolean +g_desktop_app_info_can_delete (GAppInfo *appinfo) +{ + GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); + + if (info->filename) + return g_access (info->filename, W_OK) == 0; + + return FALSE; +} + +static gboolean +g_desktop_app_info_delete (GAppInfo *appinfo) +{ + GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); + + if (info->filename) + { + if (g_remove (info->filename) == 0) + { + update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL); + + g_free (info->filename); + info->filename = NULL; + g_free (info->desktop_id); + info->desktop_id = NULL; + + return TRUE; + } + } + + return FALSE; +} + /** * g_app_info_create_from_commandline: * @commandline: the commandline to use @@ -1586,6 +1655,8 @@ g_desktop_app_info_iface_init (GAppInfoIface *iface) iface->add_supports_type = g_desktop_app_info_add_supports_type; iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type; iface->remove_supports_type = g_desktop_app_info_remove_supports_type; + iface->can_delete = g_desktop_app_info_can_delete; + iface->do_delete = g_desktop_app_info_delete; } static gboolean @@ -1643,6 +1714,22 @@ g_app_info_get_all_for_type (const char *content_type) return g_list_reverse (infos); } +/** + * g_app_info_reset_type_associations: + * content_type: a content type + * + * Removes all changes to the type associations done by + * g_app_info_set_as_default_for_type(), + * g_app_info_set_as_default_for_extension(), + * g_app_info_add_supports_type() of g_app_info_remove_supports_type(). + * + * Since: 2.20 + */ +void +g_app_info_reset_type_associations (const char *content_type) +{ + update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL); +} /** * g_app_info_get_default_for_type: diff --git a/gio/gio.symbols b/gio/gio.symbols index 85a30b1ce..2e49f539c 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -48,6 +48,8 @@ g_app_info_add_supports_type g_app_info_can_remove_supports_type g_app_info_remove_supports_type g_app_info_launch_default_for_uri +g_app_info_can_delete +g_app_info_delete g_app_launch_context_new g_app_launch_context_get_display g_app_launch_context_get_startup_notify_id @@ -60,6 +62,7 @@ g_app_info_get_all g_app_info_get_all_for_type g_app_info_get_default_for_type g_app_info_get_default_for_uri_scheme +g_app_info_reset_type_associations #endif #endif diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c index 0043258d4..3e669c673 100644 --- a/gio/gwin32appinfo.c +++ b/gio/gwin32appinfo.c @@ -661,3 +661,9 @@ g_app_info_get_all (void) return g_list_reverse (infos); } + +void +g_app_info_reset_type_associations (const char *content_type) +{ + /* nothing to do */ +} diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 8e08c34bd..510396f26 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -22,7 +22,8 @@ TEST_PROGS += \ g-file \ g-file-info \ data-input-stream \ - data-output-stream + data-output-stream \ + desktop-app-info if OS_UNIX TEST_PROGS += live-g-file unix-streams @@ -49,7 +50,9 @@ data_output_stream_LDADD = $(progs_ldadd) live_g_file_SOURCES = live-g-file.c live_g_file_LDADD = $(progs_ldadd) +desktop_app_info_SOURCES = desktop-app-info.c +desktop_app_info_LDADD = $(progs_ldadd) + unix_streams_SOURCES = unix-streams.c unix_streams_LDADD = $(progs_ldadd) \ $(top_builddir)/gthread/libgthread-2.0.la - diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c new file mode 100644 index 000000000..f954e6e82 --- /dev/null +++ b/gio/tests/desktop-app-info.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2008 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Matthias Clasen + */ + +#include +#include +#include +#include +#include + +static char *basedir; + +static GAppInfo * +create_app_info (const char *name) +{ + GError *error; + GAppInfo *info; + + error = NULL; + info = g_app_info_create_from_commandline ("/usr/bin/true blah", + name, + G_APP_INFO_CREATE_NONE, + &error); + g_assert (error == NULL); + + /* this is necessary to ensure that the info is saved */ + g_app_info_set_as_default_for_type (info, "application/x-blah", &error); + g_assert (error == NULL); + g_app_info_remove_supports_type (info, "application/x-blah", &error); + g_assert (error == NULL); + g_app_info_reset_type_associations ("application/x-blah"); + + return info; +} + +static void +test_delete (void) +{ + GAppInfo *info; + + const char *id; + char *filename; + gboolean res; + + info = create_app_info ("Blah"); + + id = g_app_info_get_id (info); + g_assert (id != NULL); + + filename = g_build_filename (basedir, "applications", id, NULL); + + res = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert (res); + + res = g_app_info_can_delete (info); + g_assert (res); + + res = g_app_info_delete (info); + g_assert (res); + + res = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert (!res); + + g_object_unref (info); + + if (g_file_test ("/usr/share/applications/gedit.desktop", G_FILE_TEST_EXISTS)) + { + info = (GAppInfo*)g_desktop_app_info_new ("gedit.desktop"); + g_assert (info); + + res = g_app_info_can_delete (info); + g_assert (!res); + + res = g_app_info_delete (info); + g_assert (!res); + } +} + +static void +test_default (void) +{ + GAppInfo *info, *info1, *info2, *info3; + GList *list; + GError *error = NULL; + + info1 = create_app_info ("Blah1"); + info2 = create_app_info ("Blah2"); + info3 = create_app_info ("Blah3"); + + g_app_info_set_as_default_for_type (info1, "application/x-test", &error); + g_assert (error == NULL); + + g_app_info_set_as_default_for_type (info2, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 2); + + /* check that both are in the list, info2 before info1 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now try adding something at the end */ + g_app_info_add_supports_type (info3, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 3); + + /* check that all are in the list, info2, info1, info3 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0); + + info = (GAppInfo *)list->next->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now remove info1 again */ + g_app_info_remove_supports_type (info1, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 2); + + /* check that both are in the list, info2 before info3 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now clean it all up */ + g_app_info_reset_type_associations ("application/x-test"); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (list == NULL); + + g_app_info_delete (info1); + g_app_info_delete (info2); + g_app_info_delete (info3); + + g_object_unref (info1); + g_object_unref (info2); +} + +static void +cleanup_dir_recurse (GFile *parent, GFile *root) +{ + gboolean res; + GError *error; + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *descend; + char *relative_path; + + g_assert (root != NULL); + + error = NULL; + enumerator = + g_file_enumerate_children (parent, "*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, + &error); + if (! enumerator) + return; + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + while ((info) && (!error)) + { + descend = g_file_get_child (parent, g_file_info_get_name (info)); + g_assert (descend != NULL); + relative_path = g_file_get_relative_path (root, descend); + g_assert (relative_path != NULL); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + cleanup_dir_recurse (descend, root); + + error = NULL; + res = g_file_delete (descend, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + + g_object_unref (descend); + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + } + g_assert (error == NULL); + + error = NULL; + res = g_file_enumerator_close (enumerator, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert (error == NULL); +} + +static void +cleanup_subdirs (const char *basedir) +{ + GFile *base, *file; + + base = g_file_new_for_path (basedir); + file = g_file_get_child (base, "applications"); + cleanup_dir_recurse (file, file); + g_object_unref (file); + file = g_file_get_child (base, "mime"); + cleanup_dir_recurse (file, file); + g_object_unref (file); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + basedir = g_get_current_dir (); + g_setenv ("XDG_DATA_HOME", basedir, TRUE); + cleanup_subdirs (basedir); + + g_test_add_func ("/desktop-app-info/delete", test_delete); + g_test_add_func ("/desktop-app-info/default", test_default); + + return g_test_run(); +}