1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Author: Alexander Larsson <alexl@redhat.com>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
35 #undef G_DISABLE_DEPRECATED
37 #include "gcontenttypeprivate.h"
38 #include "gdesktopappinfo.h"
41 #include "gthemedicon.h"
42 #include "gfileicon.h"
43 #include <glib/gstdio.h>
45 #include "giomodule-priv.h"
50 * SECTION:gdesktopappinfo
51 * @short_description: Application information from desktop files
52 * @include: gio/gdesktopappinfo.h
54 * #GDesktopAppInfo is an implementation of #GAppInfo based on
57 * Note that <filename><gio/gdesktopappinfo.h></filename> belongs to
58 * the UNIX-specific GIO interfaces, thus you have to use the
59 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
62 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
63 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
64 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
65 #define MIME_CACHE_GROUP "MIME Cache"
66 #define FULL_NAME_KEY "X-GNOME-FullName"
68 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
69 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type,
71 gboolean include_fallback);
72 static void mime_info_cache_reload (const char *dir);
73 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
79 * Information about an installed application from a desktop file.
81 struct _GDesktopAppInfo
83 GObject parent_instance;
89 /* FIXME: what about GenericName ? */
104 guint startup_notify : 1;
106 /* FIXME: what about StartupWMClass ? */
109 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
110 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
111 g_desktop_app_info_iface_init))
114 search_path_init (gpointer data)
117 const char * const *data_dirs;
118 const char *user_data_dir;
121 data_dirs = g_get_system_data_dirs ();
122 length = g_strv_length ((char **) data_dirs);
124 args = g_new (char *, length + 2);
127 user_data_dir = g_get_user_data_dir ();
128 args[j++] = g_build_filename (user_data_dir, "applications", NULL);
129 for (i = 0; i < length; i++)
130 args[j++] = g_build_filename (data_dirs[i],
131 "applications", NULL);
137 static const char * const *
138 get_applications_search_path (void)
140 static GOnce once_init = G_ONCE_INIT;
141 return g_once (&once_init, search_path_init, NULL);
145 g_desktop_app_info_finalize (GObject *object)
147 GDesktopAppInfo *info;
149 info = G_DESKTOP_APP_INFO (object);
151 g_free (info->desktop_id);
152 g_free (info->filename);
154 g_free (info->fullname);
155 g_free (info->comment);
156 g_free (info->icon_name);
158 g_object_unref (info->icon);
159 g_strfreev (info->only_show_in);
160 g_strfreev (info->not_show_in);
161 g_free (info->try_exec);
163 g_free (info->binary);
166 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
170 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
172 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
174 gobject_class->finalize = g_desktop_app_info_finalize;
178 g_desktop_app_info_init (GDesktopAppInfo *local)
183 binary_from_exec (const char *exec)
185 const char *p, *start;
191 while (*p != ' ' && *p != 0)
194 return g_strndup (start, p - start);
199 * g_desktop_app_info_new_from_keyfile:
200 * @key_file: an opened #GKeyFile
202 * Creates a new #GDesktopAppInfo.
204 * Returns: a new #GDesktopAppInfo or %NULL on error.
209 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
211 GDesktopAppInfo *info;
216 start_group = g_key_file_get_start_group (key_file);
217 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
219 g_free (start_group);
222 g_free (start_group);
224 type = g_key_file_get_string (key_file,
225 G_KEY_FILE_DESKTOP_GROUP,
226 G_KEY_FILE_DESKTOP_KEY_TYPE,
228 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
235 try_exec = g_key_file_get_string (key_file,
236 G_KEY_FILE_DESKTOP_GROUP,
237 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
239 if (try_exec && try_exec[0] != '\0')
242 t = g_find_program_in_path (try_exec);
251 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
252 info->filename = NULL;
254 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
255 info->fullname = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, FULL_NAME_KEY, NULL, NULL);
256 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
257 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
258 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
259 info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
260 info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
261 info->try_exec = try_exec;
262 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
263 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
264 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
265 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
266 info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
267 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
272 if (g_path_is_absolute (info->icon_name))
276 file = g_file_new_for_path (info->icon_name);
277 info->icon = g_file_icon_new (file);
278 g_object_unref (file);
284 /* Work around a common mistake in desktop files */
285 if ((p = strrchr (info->icon_name, '.')) != NULL &&
286 (strcmp (p, ".png") == 0 ||
287 strcmp (p, ".xpm") == 0 ||
288 strcmp (p, ".svg") == 0))
291 info->icon = g_themed_icon_new (info->icon_name);
296 info->binary = binary_from_exec (info->exec);
298 if (info->path && info->path[0] == '\0')
308 * g_desktop_app_info_new_from_filename:
309 * @filename: the path of a desktop file, in the GLib filename encoding
311 * Creates a new #GDesktopAppInfo.
313 * Returns: a new #GDesktopAppInfo or %NULL on error.
316 g_desktop_app_info_new_from_filename (const char *filename)
319 GDesktopAppInfo *info = NULL;
321 key_file = g_key_file_new ();
323 if (g_key_file_load_from_file (key_file,
328 info = g_desktop_app_info_new_from_keyfile (key_file);
330 info->filename = g_strdup (filename);
333 g_key_file_free (key_file);
339 * g_desktop_app_info_new:
340 * @desktop_id: the desktop file id
342 * Creates a new #GDesktopAppInfo based on a desktop file id.
344 * A desktop file id is the basename of the desktop file, including the
345 * .desktop extension. GIO is looking for a desktop file with this name
346 * in the <filename>applications</filename> subdirectories of the XDG data
347 * directories (i.e. the directories specified in the
348 * <envar>XDG_DATA_HOME</envar> and <envar>XDG_DATA_DIRS</envar> environment
349 * variables). GIO also supports the prefix-to-subdirectory mapping that is
350 * described in the <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Menu Spec</ulink>
351 * (i.e. a desktop id of kde-foo.desktop will match
352 * <filename>/usr/share/applications/kde/foo.desktop</filename>).
354 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
357 g_desktop_app_info_new (const char *desktop_id)
359 GDesktopAppInfo *appinfo;
360 const char * const *dirs;
364 dirs = get_applications_search_path ();
366 basename = g_strdup (desktop_id);
368 for (i = 0; dirs[i] != NULL; i++)
373 filename = g_build_filename (dirs[i], desktop_id, NULL);
374 appinfo = g_desktop_app_info_new_from_filename (filename);
380 while ((p = strchr (p, '-')) != NULL)
384 filename = g_build_filename (dirs[i], basename, NULL);
385 appinfo = g_desktop_app_info_new_from_filename (filename);
400 appinfo->desktop_id = g_strdup (desktop_id);
402 if (g_desktop_app_info_get_is_hidden (appinfo))
404 g_object_unref (appinfo);
412 g_desktop_app_info_dup (GAppInfo *appinfo)
414 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
415 GDesktopAppInfo *new_info;
417 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
419 new_info->filename = g_strdup (info->filename);
420 new_info->desktop_id = g_strdup (info->desktop_id);
422 new_info->name = g_strdup (info->name);
423 new_info->fullname = g_strdup (info->fullname);
424 new_info->comment = g_strdup (info->comment);
425 new_info->nodisplay = info->nodisplay;
426 new_info->icon_name = g_strdup (info->icon_name);
428 new_info->icon = g_object_ref (info->icon);
429 new_info->only_show_in = g_strdupv (info->only_show_in);
430 new_info->not_show_in = g_strdupv (info->not_show_in);
431 new_info->try_exec = g_strdup (info->try_exec);
432 new_info->exec = g_strdup (info->exec);
433 new_info->binary = g_strdup (info->binary);
434 new_info->path = g_strdup (info->path);
435 new_info->hidden = info->hidden;
436 new_info->terminal = info->terminal;
437 new_info->startup_notify = info->startup_notify;
439 return G_APP_INFO (new_info);
443 g_desktop_app_info_equal (GAppInfo *appinfo1,
446 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
447 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
449 if (info1->desktop_id == NULL ||
450 info2->desktop_id == NULL)
451 return info1 == info2;
453 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
457 g_desktop_app_info_get_id (GAppInfo *appinfo)
459 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
461 return info->desktop_id;
465 g_desktop_app_info_get_name (GAppInfo *appinfo)
467 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
469 if (info->name == NULL)
475 g_desktop_app_info_get_display_name (GAppInfo *appinfo)
477 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
479 if (info->fullname == NULL)
480 return g_desktop_app_info_get_name (appinfo);
481 return info->fullname;
485 * g_desktop_app_info_get_is_hidden:
486 * @info: a #GDesktopAppInfo.
488 * A desktop file is hidden if the Hidden key in it is
491 * Returns: %TRUE if hidden, %FALSE otherwise.
494 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
500 * g_desktop_app_info_get_filename:
501 * @info: a #GDesktopAppInfo
503 * When @info was created from a known filename, return it. In some
504 * situations such as the #GDesktopAppInfo returned from
505 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
507 * Returns: The full path to the file for @info, or %NULL if not known.
511 g_desktop_app_info_get_filename (GDesktopAppInfo *info)
513 return info->filename;
517 g_desktop_app_info_get_description (GAppInfo *appinfo)
519 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
521 return info->comment;
525 g_desktop_app_info_get_executable (GAppInfo *appinfo)
527 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
533 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
535 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
541 g_desktop_app_info_get_icon (GAppInfo *appinfo)
543 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
549 expand_macro_single (char macro, char *uri)
555 file = g_file_new_for_uri (uri);
556 path = g_file_get_path (file);
557 g_object_unref (file);
563 result = g_shell_quote (uri);
568 result = g_shell_quote (path);
574 name = g_path_get_dirname (path);
575 result = g_shell_quote (name);
583 name = g_path_get_basename (path);
584 result = g_shell_quote (name);
596 expand_macro (char macro,
598 GDesktopAppInfo *info,
601 GList *uris = *uri_list;
603 gboolean force_file_uri;
604 char force_file_uri_macro;
607 g_return_if_fail (exec != NULL);
609 /* On %u and %U, pass POSIX file path pointing to the URI via
610 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
611 * running or the URI doesn't have a POSIX file path via FUSE
612 * we'll just pass the URI.
614 force_file_uri_macro = macro;
615 force_file_uri = FALSE;
621 force_file_uri_macro = 'f';
622 force_file_uri = TRUE;
625 force_file_uri_macro = 'F';
626 force_file_uri = TRUE;
642 if (!force_file_uri ||
643 /* Pass URI if it contains an anchor */
644 strchr (uri, '#') != NULL)
646 expanded = expand_macro_single (macro, uri);
650 expanded = expand_macro_single (force_file_uri_macro, uri);
651 if (expanded == NULL)
652 expanded = expand_macro_single (macro, uri);
657 g_string_append (exec, expanded);
673 if (!force_file_uri ||
674 /* Pass URI if it contains an anchor */
675 strchr (uri, '#') != NULL)
677 expanded = expand_macro_single (macro, uri);
681 expanded = expand_macro_single (force_file_uri_macro, uri);
682 if (expanded == NULL)
683 expanded = expand_macro_single (macro, uri);
688 g_string_append (exec, expanded);
694 if (uris != NULL && expanded)
695 g_string_append_c (exec, ' ');
703 g_string_append (exec, "--icon ");
704 expanded = g_shell_quote (info->icon_name);
705 g_string_append (exec, expanded);
713 expanded = g_shell_quote (info->name);
714 g_string_append (exec, expanded);
722 expanded = g_shell_quote (info->filename);
723 g_string_append (exec, expanded);
728 case 'm': /* deprecated */
732 g_string_append_c (exec, '%');
740 expand_application_parameters (GDesktopAppInfo *info,
746 GList *uri_list = *uris;
747 const char *p = info->exec;
748 GString *expanded_exec;
751 if (info->exec == NULL)
753 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
754 _("Desktop file didn't specify Exec field"));
758 expanded_exec = g_string_new (NULL);
762 if (p[0] == '%' && p[1] != '\0')
764 expand_macro (p[1], expanded_exec, info, uris);
768 g_string_append_c (expanded_exec, *p);
773 /* No file substitutions */
774 if (uri_list == *uris && uri_list != NULL)
776 /* If there is no macro default to %f. This is also what KDE does */
777 g_string_append_c (expanded_exec, ' ');
778 expand_macro ('f', expanded_exec, info, uris);
781 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
782 g_string_free (expanded_exec, TRUE);
787 prepend_terminal_to_vector (int *argc,
794 char **term_argv = NULL;
799 g_return_val_if_fail (argc != NULL, FALSE);
800 g_return_val_if_fail (argv != NULL, FALSE);
808 /* compute size if not given */
811 for (i = 0; the_argv[i] != NULL; i++)
817 term_argv = g_new0 (char *, 3);
819 check = g_find_program_in_path ("gnome-terminal");
822 term_argv[0] = check;
823 /* Note that gnome-terminal takes -x and
824 * as -e in gnome-terminal is broken we use that. */
825 term_argv[1] = g_strdup ("-x");
830 check = g_find_program_in_path ("nxterm");
832 check = g_find_program_in_path ("color-xterm");
834 check = g_find_program_in_path ("rxvt");
836 check = g_find_program_in_path ("xterm");
838 check = g_find_program_in_path ("dtterm");
841 check = g_strdup ("xterm");
842 g_warning ("couldn't find a terminal, falling back to xterm");
844 term_argv[0] = check;
845 term_argv[1] = g_strdup ("-e");
848 real_argc = term_argc + *argc;
849 real_argv = g_new (char *, real_argc + 1);
851 for (i = 0; i < term_argc; i++)
852 real_argv[i] = term_argv[i];
854 for (j = 0; j < *argc; j++, i++)
855 real_argv[i] = (char *)the_argv[j];
863 /* we use g_free here as we sucked all the inner strings
864 * out from it into real_argv */
869 #endif /* G_OS_WIN32 */
873 uri_list_segment_to_files (GList *start,
880 while (start != NULL && start != end)
882 file = g_file_new_for_uri ((char *)start->data);
883 res = g_list_prepend (res, file);
887 return g_list_reverse (res);
898 child_setup (gpointer user_data)
900 ChildSetupData *data = user_data;
903 g_setenv ("DISPLAY", data->display, TRUE);
906 g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
908 if (data->desktop_file)
912 g_setenv ("GIO_LAUNCHED_DESKTOP_FILE", data->desktop_file, TRUE);
914 g_snprintf (pid, 20, "%ld", (long)getpid ());
915 g_setenv ("GIO_LAUNCHED_DESKTOP_FILE_PID", pid, TRUE);
920 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
922 GAppLaunchContext *launch_context,
925 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
926 gboolean completed = FALSE;
928 GList *launched_files;
933 g_return_val_if_fail (appinfo != NULL, FALSE);
940 if (!expand_application_parameters (info, &uris,
941 &argc, &argv, error))
944 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
946 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
947 _("Unable to find terminal required for application"));
953 data.desktop_file = info->filename;
957 launched_files = uri_list_segment_to_files (old_uris, uris);
959 data.display = g_app_launch_context_get_display (launch_context,
963 if (info->startup_notify)
964 data.sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
967 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
968 g_list_free (launched_files);
971 if (!g_spawn_async (info->path,
981 g_app_launch_context_launch_failed (launch_context, data.sn_id);
984 g_free (data.display);
990 g_free (data.display);
995 while (uris != NULL);
1006 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1008 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1010 return info->exec &&
1011 ((strstr (info->exec, "%u") != NULL) ||
1012 (strstr (info->exec, "%U") != NULL));
1016 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1018 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1020 return info->exec &&
1021 ((strstr (info->exec, "%f") != NULL) ||
1022 (strstr (info->exec, "%F") != NULL));
1026 g_desktop_app_info_launch (GAppInfo *appinfo,
1028 GAppLaunchContext *launch_context,
1038 uri = g_file_get_uri (files->data);
1039 uris = g_list_prepend (uris, uri);
1040 files = files->next;
1043 uris = g_list_reverse (uris);
1045 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1047 g_list_foreach (uris, (GFunc)g_free, NULL);
1053 G_LOCK_DEFINE_STATIC (g_desktop_env);
1054 static gchar *g_desktop_env = NULL;
1057 * g_desktop_app_info_set_desktop_env:
1058 * @desktop_env: a string specifying what desktop this is
1060 * Sets the name of the desktop that the application is running in.
1061 * This is used by g_app_info_should_show() to evaluate the
1062 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1063 * desktop entry fields.
1065 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1066 * Menu specification</ulink> recognizes the following:
1068 * <member>GNOME</member>
1069 * <member>KDE</member>
1070 * <member>ROX</member>
1071 * <member>XFCE</member>
1072 * <member>Old</member>
1075 * Should be called only once; subsequent calls are ignored.
1078 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1080 G_LOCK (g_desktop_env);
1082 g_desktop_env = g_strdup (desktop_env);
1083 G_UNLOCK (g_desktop_env);
1087 g_desktop_app_info_should_show (GAppInfo *appinfo)
1089 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1091 const gchar *desktop_env;
1094 if (info->nodisplay)
1097 G_LOCK (g_desktop_env);
1098 desktop_env = g_desktop_env;
1099 G_UNLOCK (g_desktop_env);
1101 if (info->only_show_in)
1103 if (desktop_env == NULL)
1107 for (i = 0; info->only_show_in[i] != NULL; i++)
1109 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1119 if (info->not_show_in && desktop_env)
1121 for (i = 0; info->not_show_in[i] != NULL; i++)
1123 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1137 ensure_dir (DirType type,
1140 char *path, *display_name;
1143 if (type == APP_DIR)
1144 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1146 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1149 if (g_mkdir_with_parents (path, 0700) == 0)
1153 display_name = g_filename_display_name (path);
1154 if (type == APP_DIR)
1155 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1156 _("Can't create user application configuration folder %s: %s"),
1157 display_name, g_strerror (errsv));
1159 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1160 _("Can't create user MIME configuration folder %s: %s"),
1161 display_name, g_strerror (errsv));
1163 g_free (display_name);
1170 update_mimeapps_list (const char *desktop_id,
1171 const char *content_type,
1172 gboolean add_as_default,
1173 gboolean add_non_default,
1177 char *dirname, *filename;
1179 gboolean load_succeeded, res;
1180 char **old_list, **list;
1181 GList *system_list, *l;
1182 gsize length, data_size;
1185 char **content_types;
1187 /* Don't add both at start and end */
1188 g_assert (!(add_as_default && add_non_default));
1190 dirname = ensure_dir (APP_DIR, error);
1194 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1197 key_file = g_key_file_new ();
1198 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1199 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1201 g_key_file_free (key_file);
1202 key_file = g_key_file_new ();
1207 content_types = g_new (char *, 2);
1208 content_types[0] = g_strdup (content_type);
1209 content_types[1] = NULL;
1213 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1216 for (k = 0; content_types && content_types[k]; k++)
1218 /* Add to the right place in the list */
1221 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1222 content_types[k], &length, NULL);
1224 list = g_new (char *, 1 + length + 1);
1228 list[i++] = g_strdup (desktop_id);
1231 for (j = 0; old_list[j] != NULL; j++)
1233 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1234 list[i++] = g_strdup (old_list[j]);
1235 else if (add_non_default)
1237 /* If adding as non-default, and it's already in,
1238 don't change order of desktop ids */
1239 add_non_default = FALSE;
1240 list[i++] = g_strdup (old_list[j]);
1245 if (add_non_default)
1247 /* We're adding as non-default, and it wasn't already in the list,
1248 so we add at the end. But to avoid listing the app before the
1249 current system default (thus changing the default) we have to
1250 add the current list of (not yet listed) apps before it. */
1252 list[i] = NULL; /* Terminate current list so we can use it */
1253 system_list = get_all_desktop_entries_for_mime_type (content_type, (const char **)list, FALSE);
1255 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1257 for (l = system_list; l != NULL; l = l->next)
1259 list[i++] = l->data; /* no strdup, taking ownership */
1260 if (g_strcmp0 (l->data, desktop_id) == 0)
1261 add_non_default = FALSE;
1263 g_list_free (system_list);
1265 if (add_non_default)
1266 list[i++] = g_strdup (desktop_id);
1271 g_strfreev (old_list);
1273 if (list[0] == NULL || desktop_id == NULL)
1274 g_key_file_remove_key (key_file,
1275 ADDED_ASSOCIATIONS_GROUP,
1279 g_key_file_set_string_list (key_file,
1280 ADDED_ASSOCIATIONS_GROUP,
1282 (const char * const *)list, i);
1289 /* reuse the list from above */
1293 g_strfreev (content_types);
1294 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1297 for (k = 0; content_types && content_types[k]; k++)
1299 /* Remove from removed associations group (unless remove) */
1302 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1303 content_types[k], &length, NULL);
1305 list = g_new (char *, 1 + length + 1);
1309 list[i++] = g_strdup (desktop_id);
1312 for (j = 0; old_list[j] != NULL; j++)
1314 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1315 list[i++] = g_strdup (old_list[j]);
1320 g_strfreev (old_list);
1322 if (list[0] == NULL || desktop_id == NULL)
1323 g_key_file_remove_key (key_file,
1324 REMOVED_ASSOCIATIONS_GROUP,
1328 g_key_file_set_string_list (key_file,
1329 REMOVED_ASSOCIATIONS_GROUP,
1331 (const char * const *)list, i);
1336 g_strfreev (content_types);
1338 data = g_key_file_to_data (key_file, &data_size, error);
1339 g_key_file_free (key_file);
1341 res = g_file_set_contents (filename, data, data_size, error);
1343 mime_info_cache_reload (NULL);
1352 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1353 const char *content_type,
1356 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1358 if (!g_desktop_app_info_ensure_saved (info, error))
1361 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1365 update_program_done (GPid pid,
1369 /* Did the application exit correctly */
1370 if (WIFEXITED (status) &&
1371 WEXITSTATUS (status) == 0)
1373 /* Here we could clean out any caches in use */
1378 run_update_command (char *command,
1387 GError *error = NULL;
1390 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1392 if (g_spawn_async ("/", argv,
1394 G_SPAWN_SEARCH_PATH |
1395 G_SPAWN_STDOUT_TO_DEV_NULL |
1396 G_SPAWN_STDERR_TO_DEV_NULL |
1397 G_SPAWN_DO_NOT_REAP_CHILD,
1398 NULL, NULL, /* No setup function */
1401 g_child_watch_add (pid, update_program_done, NULL);
1404 /* If we get an error at this point, it's quite likely the user doesn't
1405 * have an installed copy of either 'update-mime-database' or
1406 * 'update-desktop-database'. I don't think we want to popup an error
1407 * dialog at this point, so we just do a g_warning to give the user a
1408 * chance of debugging it.
1410 g_warning ("%s", error->message);
1417 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1418 const char *extension,
1421 char *filename, *basename, *mimetype;
1425 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1428 dirname = ensure_dir (MIMETYPE_DIR, error);
1432 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1433 filename = g_build_filename (dirname, basename, NULL);
1437 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1439 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1444 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1445 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1446 " <mime-type type=\"%s\">\n"
1447 " <comment>%s document</comment>\n"
1448 " <glob pattern=\"*.%s\"/>\n"
1450 "</mime-info>\n", mimetype, extension, extension);
1452 g_file_set_contents (filename, contents, -1, NULL);
1455 run_update_command ("update-mime-database", "mime");
1459 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1469 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1470 const char *content_type,
1473 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1475 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1478 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1482 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1488 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1489 const char *content_type,
1492 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1494 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1497 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1501 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1507 char *data, *desktop_id;
1512 if (info->filename != NULL)
1515 /* This is only used for object created with
1516 * g_app_info_create_from_commandline. All other
1517 * object should have a filename
1520 dirname = ensure_dir (APP_DIR, error);
1524 key_file = g_key_file_new ();
1526 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1527 "Encoding", "UTF-8");
1528 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1529 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1530 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1531 G_KEY_FILE_DESKTOP_KEY_TYPE,
1532 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1534 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1535 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1537 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1538 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1540 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1541 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1543 if (info->fullname != NULL)
1544 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1545 FULL_NAME_KEY, info->fullname);
1547 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1548 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1550 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1551 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1553 data = g_key_file_to_data (key_file, &data_size, NULL);
1554 g_key_file_free (key_file);
1556 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1557 filename = g_build_filename (dirname, desktop_id, NULL);
1558 g_free (desktop_id);
1561 fd = g_mkstemp (filename);
1566 display_name = g_filename_display_name (filename);
1567 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1568 _("Can't create user desktop file %s"), display_name);
1569 g_free (display_name);
1575 desktop_id = g_path_get_basename (filename);
1579 res = g_file_set_contents (filename, data, data_size, error);
1582 g_free (desktop_id);
1587 info->filename = filename;
1588 info->desktop_id = desktop_id;
1590 run_update_command ("update-desktop-database", "applications");
1596 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1598 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1602 if (strstr (info->filename, "/userapp-"))
1603 return g_access (info->filename, W_OK) == 0;
1610 g_desktop_app_info_delete (GAppInfo *appinfo)
1612 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1616 if (g_remove (info->filename) == 0)
1618 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1620 g_free (info->filename);
1621 info->filename = NULL;
1622 g_free (info->desktop_id);
1623 info->desktop_id = NULL;
1633 * g_app_info_create_from_commandline:
1634 * @commandline: the commandline to use
1635 * @application_name: (allow-none): the application name, or %NULL to use @commandline
1636 * @flags: flags that can specify details of the created #GAppInfo
1637 * @error: a #GError location to store the error occuring, %NULL to ignore.
1639 * Creates a new #GAppInfo from the given information.
1641 * Returns: (transfer full): new #GAppInfo for given command.
1644 g_app_info_create_from_commandline (const char *commandline,
1645 const char *application_name,
1646 GAppInfoCreateFlags flags,
1651 GDesktopAppInfo *info;
1653 g_return_val_if_fail (commandline, NULL);
1655 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1657 info->filename = NULL;
1658 info->desktop_id = NULL;
1660 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1661 info->startup_notify = flags & G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION;
1662 info->hidden = FALSE;
1663 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1664 info->exec = g_strconcat (commandline, " %u", NULL);
1666 info->exec = g_strconcat (commandline, " %f", NULL);
1667 info->nodisplay = TRUE;
1668 info->binary = binary_from_exec (info->exec);
1670 if (application_name)
1671 info->name = g_strdup (application_name);
1674 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1675 split = g_strsplit (commandline, " ", 2);
1676 basename = split[0] ? g_path_get_basename (split[0]) : NULL;
1678 info->name = basename;
1679 if (info->name == NULL)
1680 info->name = g_strdup ("custom");
1682 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1684 return G_APP_INFO (info);
1688 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1690 iface->dup = g_desktop_app_info_dup;
1691 iface->equal = g_desktop_app_info_equal;
1692 iface->get_id = g_desktop_app_info_get_id;
1693 iface->get_name = g_desktop_app_info_get_name;
1694 iface->get_description = g_desktop_app_info_get_description;
1695 iface->get_executable = g_desktop_app_info_get_executable;
1696 iface->get_icon = g_desktop_app_info_get_icon;
1697 iface->launch = g_desktop_app_info_launch;
1698 iface->supports_uris = g_desktop_app_info_supports_uris;
1699 iface->supports_files = g_desktop_app_info_supports_files;
1700 iface->launch_uris = g_desktop_app_info_launch_uris;
1701 iface->should_show = g_desktop_app_info_should_show;
1702 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1703 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1704 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1705 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1706 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1707 iface->can_delete = g_desktop_app_info_can_delete;
1708 iface->do_delete = g_desktop_app_info_delete;
1709 iface->get_commandline = g_desktop_app_info_get_commandline;
1710 iface->get_display_name = g_desktop_app_info_get_display_name;
1714 app_info_in_list (GAppInfo *info,
1717 while (list != NULL)
1719 if (g_app_info_equal (info, list->data))
1727 * g_app_info_get_recommended_for_type:
1728 * @content_type: the content type to find a #GAppInfo for
1730 * Gets a list of recommended #GAppInfo<!-- -->s for a given content type, i.e.
1731 * those applications which claim to support the given content type exactly, and
1732 * not by MIME type subclassing.
1734 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfo<!-- -->s for given @content_type
1735 * or %NULL on error.
1738 g_app_info_get_recommended_for_type (const gchar *content_type)
1740 GList *desktop_entries, *l;
1742 GDesktopAppInfo *info;
1744 g_return_val_if_fail (content_type != NULL, NULL);
1746 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, FALSE);
1749 for (l = desktop_entries; l != NULL; l = l->next)
1751 char *desktop_entry = l->data;
1753 info = g_desktop_app_info_new (desktop_entry);
1756 if (app_info_in_list (G_APP_INFO (info), infos))
1757 g_object_unref (info);
1759 infos = g_list_prepend (infos, info);
1761 g_free (desktop_entry);
1764 g_list_free (desktop_entries);
1766 return g_list_reverse (infos);
1770 * g_app_info_get_fallback_for_type:
1771 * @content_type: the content type to find a #GAppInfo for
1773 * Gets a list of fallback #GAppInfo<!-- -->s for a given content type, i.e.
1774 * those applications which claim to support the given content type by MIME type
1775 * subclassing and not directly.
1777 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfo<!-- -->s for given @content_type
1778 * or %NULL on error.
1781 g_app_info_get_fallback_for_type (const gchar *content_type)
1783 GList *desktop_entries, *l;
1784 GList *infos, *recommended_infos;
1785 GDesktopAppInfo *info;
1787 g_return_val_if_fail (content_type != NULL, NULL);
1789 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE);
1790 recommended_infos = g_app_info_get_recommended_for_type (content_type);
1793 for (l = desktop_entries; l != NULL; l = l->next)
1795 char *desktop_entry = l->data;
1797 info = g_desktop_app_info_new (desktop_entry);
1800 if (app_info_in_list (G_APP_INFO (info), infos) ||
1801 app_info_in_list (G_APP_INFO (info), recommended_infos))
1802 g_object_unref (info);
1804 infos = g_list_prepend (infos, info);
1806 g_free (desktop_entry);
1809 g_list_free (desktop_entries);
1810 g_list_free_full (recommended_infos, g_object_unref);
1812 return g_list_reverse (infos);
1816 * g_app_info_get_all_for_type:
1817 * @content_type: the content type to find a #GAppInfo for
1819 * Gets a list of all #GAppInfo<!-- -->s for a given content type.
1821 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfo<!-- -->s for given @content_type
1822 * or %NULL on error.
1825 g_app_info_get_all_for_type (const char *content_type)
1827 GList *desktop_entries, *l;
1829 GDesktopAppInfo *info;
1831 g_return_val_if_fail (content_type != NULL, NULL);
1833 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE);
1836 for (l = desktop_entries; l != NULL; l = l->next)
1838 char *desktop_entry = l->data;
1840 info = g_desktop_app_info_new (desktop_entry);
1843 if (app_info_in_list (G_APP_INFO (info), infos))
1844 g_object_unref (info);
1846 infos = g_list_prepend (infos, info);
1848 g_free (desktop_entry);
1851 g_list_free (desktop_entries);
1853 return g_list_reverse (infos);
1857 * g_app_info_reset_type_associations:
1858 * @content_type: a content type
1860 * Removes all changes to the type associations done by
1861 * g_app_info_set_as_default_for_type(),
1862 * g_app_info_set_as_default_for_extension(),
1863 * g_app_info_add_supports_type() or g_app_info_remove_supports_type().
1868 g_app_info_reset_type_associations (const char *content_type)
1870 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1874 * g_app_info_get_default_for_type:
1875 * @content_type: the content type to find a #GAppInfo for
1876 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1879 * Gets the #GAppInfo that corresponds to a given content type.
1881 * Returns: #GAppInfo for given @content_type or %NULL on error.
1884 g_app_info_get_default_for_type (const char *content_type,
1885 gboolean must_support_uris)
1887 GList *desktop_entries, *l;
1890 g_return_val_if_fail (content_type != NULL, NULL);
1892 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE);
1895 for (l = desktop_entries; l != NULL; l = l->next)
1897 char *desktop_entry = l->data;
1899 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1902 if (must_support_uris && !g_app_info_supports_uris (info))
1904 g_object_unref (info);
1912 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1913 g_list_free (desktop_entries);
1919 * g_app_info_get_default_for_uri_scheme:
1920 * @uri_scheme: a string containing a URI scheme.
1922 * Gets the default application for launching applications
1923 * using this URI scheme. A URI scheme is the initial part
1924 * of the URI, up to but not including the ':', e.g. "http",
1927 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1930 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1933 char *content_type, *scheme_down;
1935 scheme_down = g_ascii_strdown (uri_scheme, -1);
1936 content_type = g_strdup_printf ("x-scheme-handler/%s", scheme_down);
1937 g_free (scheme_down);
1938 app_info = g_app_info_get_default_for_type (content_type, FALSE);
1939 g_free (content_type);
1945 get_apps_from_dir (GHashTable *apps,
1946 const char *dirname,
1950 const char *basename;
1951 char *filename, *subprefix, *desktop_id;
1953 GDesktopAppInfo *appinfo;
1955 dir = g_dir_open (dirname, 0, NULL);
1958 while ((basename = g_dir_read_name (dir)) != NULL)
1960 filename = g_build_filename (dirname, basename, NULL);
1961 if (g_str_has_suffix (basename, ".desktop"))
1963 desktop_id = g_strconcat (prefix, basename, NULL);
1965 /* Use _extended so we catch NULLs too (hidden) */
1966 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1968 appinfo = g_desktop_app_info_new_from_filename (filename);
1971 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1973 g_object_unref (appinfo);
1978 if (appinfo || hidden)
1980 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1984 /* Reuse instead of strdup here */
1985 appinfo->desktop_id = desktop_id;
1990 g_free (desktop_id);
1994 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1996 subprefix = g_strconcat (prefix, basename, "-", NULL);
1997 get_apps_from_dir (apps, filename, subprefix);
2009 * g_app_info_get_all:
2011 * Gets a list of all of the applications currently registered
2014 * For desktop files, this includes applications that have
2015 * <literal>NoDisplay=true</literal> set or are excluded from
2016 * display by means of <literal>OnlyShowIn</literal> or
2017 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2018 * The returned list does not include applications which have
2019 * the <literal>Hidden</literal> key set.
2021 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfo<!---->s.
2024 g_app_info_get_all (void)
2026 const char * const *dirs;
2028 GHashTableIter iter;
2033 dirs = get_applications_search_path ();
2035 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2039 for (i = 0; dirs[i] != NULL; i++)
2040 get_apps_from_dir (apps, dirs[i], "");
2044 g_hash_table_iter_init (&iter, apps);
2045 while (g_hash_table_iter_next (&iter, NULL, &value))
2048 infos = g_list_prepend (infos, value);
2051 g_hash_table_destroy (apps);
2053 return g_list_reverse (infos);
2056 /* Cacheing of mimeinfo.cache and defaults.list files */
2060 GHashTable *mime_info_cache_map;
2061 GHashTable *defaults_list_map;
2062 GHashTable *mimeapps_list_added_map;
2063 GHashTable *mimeapps_list_removed_map;
2064 time_t mime_info_cache_timestamp;
2065 time_t defaults_list_timestamp;
2066 time_t mimeapps_list_timestamp;
2070 GList *dirs; /* mimeinfo.cache and defaults.list */
2071 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2072 time_t last_stat_time;
2073 guint should_ping_mime_monitor : 1;
2076 static MimeInfoCache *mime_info_cache = NULL;
2077 G_LOCK_DEFINE_STATIC (mime_info_cache);
2079 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2080 const char *mime_type,
2081 char **new_desktop_file_ids);
2083 static MimeInfoCache * mime_info_cache_new (void);
2086 destroy_info_cache_value (gpointer key,
2090 g_list_foreach (value, (GFunc)g_free, NULL);
2091 g_list_free (value);
2095 destroy_info_cache_map (GHashTable *info_cache_map)
2097 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2098 g_hash_table_destroy (info_cache_map);
2102 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2103 const char *cache_file,
2109 filename = g_build_filename (dir->path, cache_file, NULL);
2111 if (g_stat (filename, &buf) < 0)
2118 if (buf.st_mtime != *timestamp)
2124 /* Call with lock held */
2126 remove_all (gpointer key,
2135 mime_info_cache_blow_global_cache (void)
2137 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2142 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2146 gchar *filename, **mime_types;
2153 if (dir->mime_info_cache_map != NULL &&
2154 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2155 &dir->mime_info_cache_timestamp))
2158 if (dir->mime_info_cache_map != NULL)
2159 destroy_info_cache_map (dir->mime_info_cache_map);
2161 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2162 (GDestroyNotify) g_free,
2165 key_file = g_key_file_new ();
2167 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2169 if (g_stat (filename, &buf) < 0)
2172 if (dir->mime_info_cache_timestamp > 0)
2173 mime_info_cache->should_ping_mime_monitor = TRUE;
2175 dir->mime_info_cache_timestamp = buf.st_mtime;
2177 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2182 if (load_error != NULL)
2185 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2188 if (load_error != NULL)
2191 for (i = 0; mime_types[i] != NULL; i++)
2193 gchar **desktop_file_ids;
2194 char *unaliased_type;
2195 desktop_file_ids = g_key_file_get_string_list (key_file,
2201 if (desktop_file_ids == NULL)
2204 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2205 mime_info_cache_dir_add_desktop_entries (dir,
2208 g_free (unaliased_type);
2210 g_strfreev (desktop_file_ids);
2213 g_strfreev (mime_types);
2214 g_key_file_free (key_file);
2219 g_key_file_free (key_file);
2221 if (mime_types != NULL)
2222 g_strfreev (mime_types);
2225 g_error_free (load_error);
2229 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2233 gchar *filename, **mime_types;
2234 char *unaliased_type;
2235 char **desktop_file_ids;
2242 if (dir->defaults_list_map != NULL &&
2243 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2244 &dir->defaults_list_timestamp))
2247 if (dir->defaults_list_map != NULL)
2248 g_hash_table_destroy (dir->defaults_list_map);
2249 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2250 g_free, (GDestroyNotify)g_strfreev);
2253 key_file = g_key_file_new ();
2255 filename = g_build_filename (dir->path, "defaults.list", NULL);
2256 if (g_stat (filename, &buf) < 0)
2259 if (dir->defaults_list_timestamp > 0)
2260 mime_info_cache->should_ping_mime_monitor = TRUE;
2262 dir->defaults_list_timestamp = buf.st_mtime;
2264 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2268 if (load_error != NULL)
2271 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2273 if (mime_types != NULL)
2275 for (i = 0; mime_types[i] != NULL; i++)
2277 desktop_file_ids = g_key_file_get_string_list (key_file,
2278 DEFAULT_APPLICATIONS_GROUP,
2282 if (desktop_file_ids == NULL)
2285 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2286 g_hash_table_replace (dir->defaults_list_map,
2291 g_strfreev (mime_types);
2294 g_key_file_free (key_file);
2299 g_key_file_free (key_file);
2301 if (mime_types != NULL)
2302 g_strfreev (mime_types);
2305 g_error_free (load_error);
2309 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2313 gchar *filename, **mime_types;
2314 char *unaliased_type;
2315 char **desktop_file_ids;
2322 if (dir->mimeapps_list_added_map != NULL &&
2323 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2324 &dir->mimeapps_list_timestamp))
2327 if (dir->mimeapps_list_added_map != NULL)
2328 g_hash_table_destroy (dir->mimeapps_list_added_map);
2329 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2330 g_free, (GDestroyNotify)g_strfreev);
2332 if (dir->mimeapps_list_removed_map != NULL)
2333 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2334 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2335 g_free, (GDestroyNotify)g_strfreev);
2337 key_file = g_key_file_new ();
2339 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2340 if (g_stat (filename, &buf) < 0)
2343 if (dir->mimeapps_list_timestamp > 0)
2344 mime_info_cache->should_ping_mime_monitor = TRUE;
2346 dir->mimeapps_list_timestamp = buf.st_mtime;
2348 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2352 if (load_error != NULL)
2355 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2357 if (mime_types != NULL)
2359 for (i = 0; mime_types[i] != NULL; i++)
2361 desktop_file_ids = g_key_file_get_string_list (key_file,
2362 ADDED_ASSOCIATIONS_GROUP,
2366 if (desktop_file_ids == NULL)
2369 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2370 g_hash_table_replace (dir->mimeapps_list_added_map,
2375 g_strfreev (mime_types);
2378 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2380 if (mime_types != NULL)
2382 for (i = 0; mime_types[i] != NULL; i++)
2384 desktop_file_ids = g_key_file_get_string_list (key_file,
2385 REMOVED_ASSOCIATIONS_GROUP,
2389 if (desktop_file_ids == NULL)
2392 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2393 g_hash_table_replace (dir->mimeapps_list_removed_map,
2398 g_strfreev (mime_types);
2401 g_key_file_free (key_file);
2406 g_key_file_free (key_file);
2408 if (mime_types != NULL)
2409 g_strfreev (mime_types);
2412 g_error_free (load_error);
2415 static MimeInfoCacheDir *
2416 mime_info_cache_dir_new (const char *path)
2418 MimeInfoCacheDir *dir;
2420 dir = g_new0 (MimeInfoCacheDir, 1);
2421 dir->path = g_strdup (path);
2427 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2432 if (dir->mime_info_cache_map != NULL)
2434 destroy_info_cache_map (dir->mime_info_cache_map);
2435 dir->mime_info_cache_map = NULL;
2439 if (dir->defaults_list_map != NULL)
2441 g_hash_table_destroy (dir->defaults_list_map);
2442 dir->defaults_list_map = NULL;
2445 if (dir->mimeapps_list_added_map != NULL)
2447 g_hash_table_destroy (dir->mimeapps_list_added_map);
2448 dir->mimeapps_list_added_map = NULL;
2451 if (dir->mimeapps_list_removed_map != NULL)
2453 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2454 dir->mimeapps_list_removed_map = NULL;
2461 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2462 const char *mime_type,
2463 char **new_desktop_file_ids)
2465 GList *desktop_file_ids;
2468 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2471 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2473 if (!g_list_find_custom (desktop_file_ids, new_desktop_file_ids[i], (GCompareFunc) strcmp))
2474 desktop_file_ids = g_list_append (desktop_file_ids,
2475 g_strdup (new_desktop_file_ids[i]));
2478 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2482 mime_info_cache_init_dir_lists (void)
2484 const char * const *dirs;
2487 mime_info_cache = mime_info_cache_new ();
2489 dirs = get_applications_search_path ();
2491 for (i = 0; dirs[i] != NULL; i++)
2493 MimeInfoCacheDir *dir;
2495 dir = mime_info_cache_dir_new (dirs[i]);
2499 mime_info_cache_dir_init (dir);
2500 mime_info_cache_dir_init_defaults_list (dir);
2501 mime_info_cache_dir_init_mimeapps_list (dir);
2503 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2509 mime_info_cache_update_dir_lists (void)
2513 tmp = mime_info_cache->dirs;
2517 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2519 /* No need to do this if we had file monitors... */
2520 mime_info_cache_blow_global_cache ();
2521 mime_info_cache_dir_init (dir);
2522 mime_info_cache_dir_init_defaults_list (dir);
2523 mime_info_cache_dir_init_mimeapps_list (dir);
2530 mime_info_cache_init (void)
2532 G_LOCK (mime_info_cache);
2533 if (mime_info_cache == NULL)
2534 mime_info_cache_init_dir_lists ();
2540 if (now >= mime_info_cache->last_stat_time + 10)
2542 mime_info_cache_update_dir_lists ();
2543 mime_info_cache->last_stat_time = now;
2547 if (mime_info_cache->should_ping_mime_monitor)
2549 /* g_idle_add (emit_mime_changed, NULL); */
2550 mime_info_cache->should_ping_mime_monitor = FALSE;
2553 G_UNLOCK (mime_info_cache);
2556 static MimeInfoCache *
2557 mime_info_cache_new (void)
2559 MimeInfoCache *cache;
2561 cache = g_new0 (MimeInfoCache, 1);
2563 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2564 (GDestroyNotify) g_free,
2565 (GDestroyNotify) g_free);
2570 mime_info_cache_free (MimeInfoCache *cache)
2575 g_list_foreach (cache->dirs,
2576 (GFunc) mime_info_cache_dir_free,
2578 g_list_free (cache->dirs);
2579 g_hash_table_destroy (cache->global_defaults_cache);
2584 * mime_info_cache_reload:
2585 * @dir: directory path which needs reloading.
2587 * Reload the mime information for the @dir.
2590 mime_info_cache_reload (const char *dir)
2592 /* FIXME: just reload the dir that needs reloading,
2593 * don't blow the whole cache
2595 if (mime_info_cache != NULL)
2597 G_LOCK (mime_info_cache);
2598 mime_info_cache_free (mime_info_cache);
2599 mime_info_cache = NULL;
2600 G_UNLOCK (mime_info_cache);
2605 append_desktop_entry (GList *list,
2606 const char *desktop_entry,
2607 GList *removed_entries)
2609 /* Add if not already in list, and valid */
2610 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2611 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2612 list = g_list_prepend (list, g_strdup (desktop_entry));
2618 * get_all_desktop_entries_for_mime_type:
2619 * @mime_type: a mime type.
2620 * @except: NULL or a strv list
2622 * Returns all the desktop ids for @mime_type. The desktop files
2623 * are listed in an order so that default applications are listed before
2624 * non-default ones, and handlers for inherited mimetypes are listed
2625 * after the base ones.
2627 * Optionally doesn't list the desktop ids given in the @except
2629 * Return value: a #GList containing the desktop ids which claim
2630 * to handle @mime_type.
2633 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2634 const char **except,
2635 gboolean include_fallback)
2637 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2638 MimeInfoCacheDir *dir;
2641 char **default_entries;
2642 char **removed_associations;
2647 mime_info_cache_init ();
2649 if (include_fallback)
2651 /* collect all ancestors */
2652 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2653 array = g_ptr_array_new ();
2654 for (i = 0; mime_types[i]; i++)
2655 g_ptr_array_add (array, mime_types[i]);
2656 g_free (mime_types);
2657 for (i = 0; i < array->len; i++)
2659 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2660 for (j = 0; anc[j]; j++)
2662 for (k = 0; k < array->len; k++)
2664 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2667 if (k == array->len) /* not found */
2668 g_ptr_array_add (array, g_strdup (anc[j]));
2672 g_ptr_array_add (array, NULL);
2673 mime_types = (char **)g_ptr_array_free (array, FALSE);
2677 mime_types = g_malloc0 (2 * sizeof (gchar *));
2678 mime_types[0] = g_strdup (base_mime_type);
2679 mime_types[1] = NULL;
2682 G_LOCK (mime_info_cache);
2684 removed_entries = NULL;
2685 desktop_entries = NULL;
2687 for (i = 0; except != NULL && except[i] != NULL; i++)
2688 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2690 for (i = 0; mime_types[i] != NULL; i++)
2692 mime_type = mime_types[i];
2694 /* Go through all apps listed as defaults */
2695 for (dir_list = mime_info_cache->dirs;
2697 dir_list = dir_list->next)
2699 dir = dir_list->data;
2701 /* First added associations from mimeapps.list */
2702 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2703 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2704 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2706 /* Then removed associations from mimeapps.list */
2707 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2708 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2709 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2711 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2712 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2713 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2714 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2717 /* Go through all entries that support the mimetype */
2718 for (dir_list = mime_info_cache->dirs;
2720 dir_list = dir_list->next)
2722 dir = dir_list->data;
2724 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2725 for (tmp = list; tmp != NULL; tmp = tmp->next)
2726 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2730 G_UNLOCK (mime_info_cache);
2732 g_strfreev (mime_types);
2734 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2735 g_list_free (removed_entries);
2737 desktop_entries = g_list_reverse (desktop_entries);
2739 return desktop_entries;
2742 /* GDesktopAppInfoLookup interface: */
2744 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface;
2745 G_DEFINE_INTERFACE (GDesktopAppInfoLookup, g_desktop_app_info_lookup, G_TYPE_OBJECT)
2748 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface)
2753 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2754 * @lookup: a #GDesktopAppInfoLookup
2755 * @uri_scheme: a string containing a URI scheme.
2757 * Gets the default application for launching applications
2758 * using this URI scheme for a particular GDesktopAppInfoLookup
2761 * The GDesktopAppInfoLookup interface and this function is used
2762 * to implement g_app_info_get_default_for_uri_scheme() backends
2763 * in a GIO module. There is no reason for applications to use it
2764 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2766 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
2768 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
2771 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2772 const char *uri_scheme)
2774 GDesktopAppInfoLookupIface *iface;
2776 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2778 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2780 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);