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 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
39 #include "gthemedicon.h"
40 #include "gfileicon.h"
41 #include <glib/gstdio.h>
43 #include "giomodule-priv.h"
49 * SECTION:gdesktopappinfo
50 * @short_description: Application information from desktop files
51 * @include: gio/gdesktopappinfo.h
53 * #GDesktopAppInfo is an implementation of #GAppInfo based on
56 * Note that GDesktopAppInfo belongs to the UNIX-specific GIO interfaces,
57 * thus you have to use the <filename>gio-unix-2.0.pc</filename> pkg-config
61 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
62 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
63 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
64 #define MIME_CACHE_GROUP "MIME Cache"
66 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
67 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type,
69 static void mime_info_cache_reload (const char *dir);
70 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
76 * Information about an installed application from a desktop file.
78 struct _GDesktopAppInfo
80 GObject parent_instance;
86 /* FIXME: what about GenericName ? */
100 guint startup_notify : 1;
101 /* FIXME: what about StartupWMClass ? */
104 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
105 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
106 g_desktop_app_info_iface_init))
109 search_path_init (gpointer data)
112 const char * const *data_dirs;
113 const char *user_data_dir;
116 data_dirs = g_get_system_data_dirs ();
117 length = g_strv_length ((char **) data_dirs);
119 args = g_new (char *, length + 2);
122 user_data_dir = g_get_user_data_dir ();
123 args[j++] = g_build_filename (user_data_dir, "applications", NULL);
124 for (i = 0; i < length; i++)
125 args[j++] = g_build_filename (data_dirs[i],
126 "applications", NULL);
132 static const char * const *
133 get_applications_search_path (void)
135 static GOnce once_init = G_ONCE_INIT;
136 return g_once (&once_init, search_path_init, NULL);
140 g_desktop_app_info_finalize (GObject *object)
142 GDesktopAppInfo *info;
144 info = G_DESKTOP_APP_INFO (object);
146 g_free (info->desktop_id);
147 g_free (info->filename);
149 g_free (info->comment);
150 g_free (info->icon_name);
152 g_object_unref (info->icon);
153 g_strfreev (info->only_show_in);
154 g_strfreev (info->not_show_in);
155 g_free (info->try_exec);
157 g_free (info->binary);
160 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
164 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
166 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
168 gobject_class->finalize = g_desktop_app_info_finalize;
172 g_desktop_app_info_init (GDesktopAppInfo *local)
177 binary_from_exec (const char *exec)
179 const char *p, *start;
185 while (*p != ' ' && *p != 0)
188 return g_strndup (start, p - start);
193 * g_desktop_app_info_new_from_keyfile:
194 * @key_file: an opened #GKeyFile
196 * Creates a new #GDesktopAppInfo.
198 * Returns: a new #GDesktopAppInfo or %NULL on error.
203 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
205 GDesktopAppInfo *info;
210 start_group = g_key_file_get_start_group (key_file);
211 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
213 g_free (start_group);
216 g_free (start_group);
218 type = g_key_file_get_string (key_file,
219 G_KEY_FILE_DESKTOP_GROUP,
220 G_KEY_FILE_DESKTOP_KEY_TYPE,
222 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
229 try_exec = g_key_file_get_string (key_file,
230 G_KEY_FILE_DESKTOP_GROUP,
231 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
233 if (try_exec && try_exec[0] != '\0')
236 t = g_find_program_in_path (try_exec);
245 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
246 info->filename = NULL;
248 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
249 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
250 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
251 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
252 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);
253 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);
254 info->try_exec = try_exec;
255 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
256 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
257 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
258 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
259 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
264 if (g_path_is_absolute (info->icon_name))
268 file = g_file_new_for_path (info->icon_name);
269 info->icon = g_file_icon_new (file);
270 g_object_unref (file);
276 /* Work around a common mistake in desktop files */
277 if ((p = strrchr (info->icon_name, '.')) != NULL &&
278 (strcmp (p, ".png") == 0 ||
279 strcmp (p, ".xpm") == 0 ||
280 strcmp (p, ".svg") == 0))
283 info->icon = g_themed_icon_new (info->icon_name);
288 info->binary = binary_from_exec (info->exec);
290 if (info->path && info->path[0] == '\0')
300 * g_desktop_app_info_new_from_filename:
301 * @filename: the path of a desktop file, in the GLib filename encoding
303 * Creates a new #GDesktopAppInfo.
305 * Returns: a new #GDesktopAppInfo or %NULL on error.
308 g_desktop_app_info_new_from_filename (const char *filename)
311 GDesktopAppInfo *info = NULL;
313 key_file = g_key_file_new ();
315 if (g_key_file_load_from_file (key_file,
320 info = g_desktop_app_info_new_from_keyfile (key_file);
322 info->filename = g_strdup (filename);
325 g_key_file_free (key_file);
331 * g_desktop_app_info_new:
332 * @desktop_id: the desktop file id
334 * Creates a new #GDesktopAppInfo.
336 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
339 g_desktop_app_info_new (const char *desktop_id)
341 GDesktopAppInfo *appinfo;
342 const char * const *dirs;
346 dirs = get_applications_search_path ();
348 basename = g_strdup (desktop_id);
350 for (i = 0; dirs[i] != NULL; i++)
355 filename = g_build_filename (dirs[i], desktop_id, NULL);
356 appinfo = g_desktop_app_info_new_from_filename (filename);
362 while ((p = strchr (p, '-')) != NULL)
366 filename = g_build_filename (dirs[i], basename, NULL);
367 appinfo = g_desktop_app_info_new_from_filename (filename);
382 appinfo->desktop_id = g_strdup (desktop_id);
384 if (g_desktop_app_info_get_is_hidden (appinfo))
386 g_object_unref (appinfo);
394 g_desktop_app_info_dup (GAppInfo *appinfo)
396 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
397 GDesktopAppInfo *new_info;
399 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
401 new_info->filename = g_strdup (info->filename);
402 new_info->desktop_id = g_strdup (info->desktop_id);
404 new_info->name = g_strdup (info->name);
405 new_info->comment = g_strdup (info->comment);
406 new_info->nodisplay = info->nodisplay;
407 new_info->icon_name = g_strdup (info->icon_name);
408 new_info->icon = g_object_ref (info->icon);
409 new_info->only_show_in = g_strdupv (info->only_show_in);
410 new_info->not_show_in = g_strdupv (info->not_show_in);
411 new_info->try_exec = g_strdup (info->try_exec);
412 new_info->exec = g_strdup (info->exec);
413 new_info->binary = g_strdup (info->binary);
414 new_info->path = g_strdup (info->path);
415 new_info->hidden = info->hidden;
416 new_info->terminal = info->terminal;
417 new_info->startup_notify = info->startup_notify;
419 return G_APP_INFO (new_info);
423 g_desktop_app_info_equal (GAppInfo *appinfo1,
426 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
427 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
429 if (info1->desktop_id == NULL ||
430 info2->desktop_id == NULL)
431 return info1 == info2;
433 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
437 g_desktop_app_info_get_id (GAppInfo *appinfo)
439 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
441 return info->desktop_id;
445 g_desktop_app_info_get_name (GAppInfo *appinfo)
447 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
449 if (info->name == NULL)
455 * g_desktop_app_info_get_is_hidden:
456 * @info: a #GDesktopAppInfo.
458 * A desktop file is hidden if the Hidden key in it is
461 * Returns: %TRUE if hidden, %FALSE otherwise.
464 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
470 g_desktop_app_info_get_description (GAppInfo *appinfo)
472 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
474 return info->comment;
478 g_desktop_app_info_get_executable (GAppInfo *appinfo)
480 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
486 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
488 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
494 g_desktop_app_info_get_icon (GAppInfo *appinfo)
496 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
502 expand_macro_single (char macro, char *uri)
508 file = g_file_new_for_uri (uri);
509 path = g_file_get_path (file);
510 g_object_unref (file);
516 result = g_shell_quote (uri);
521 result = g_shell_quote (path);
527 name = g_path_get_dirname (path);
528 result = g_shell_quote (name);
536 name = g_path_get_basename (path);
537 result = g_shell_quote (name);
549 expand_macro (char macro,
551 GDesktopAppInfo *info,
554 GList *uris = *uri_list;
556 gboolean force_file_uri;
557 char force_file_uri_macro;
559 g_return_if_fail (exec != NULL);
561 /* On %u and %U, pass POSIX file path pointing to the URI via
562 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
563 * running or the URI doesn't have a POSIX file path via FUSE
564 * we'll just pass the URI.
569 force_file_uri_macro = 'f';
570 force_file_uri = TRUE;
573 force_file_uri_macro = 'F';
574 force_file_uri = TRUE;
577 force_file_uri_macro = macro;
578 force_file_uri = FALSE;
592 expanded = expand_macro_single (macro, uris->data);
596 expanded = expand_macro_single (force_file_uri_macro, uris->data);
597 if (expanded == NULL)
598 expanded = expand_macro_single (macro, uris->data);
603 g_string_append (exec, expanded);
619 expanded = expand_macro_single (macro, uris->data);
623 expanded = expand_macro_single (force_file_uri_macro, uris->data);
624 if (expanded == NULL)
625 expanded = expand_macro_single (macro, uris->data);
630 g_string_append (exec, expanded);
636 if (uris != NULL && expanded)
637 g_string_append_c (exec, ' ');
645 g_string_append (exec, "--icon ");
646 g_string_append (exec, info->icon_name);
652 g_string_append (exec, info->name);
657 g_string_append (exec, info->filename);
660 case 'm': /* deprecated */
664 g_string_append_c (exec, '%');
672 expand_application_parameters (GDesktopAppInfo *info,
678 GList *uri_list = *uris;
679 const char *p = info->exec;
680 GString *expanded_exec = g_string_new (NULL);
683 if (info->exec == NULL)
685 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
686 _("Desktop file didn't specify Exec field"));
692 if (p[0] == '%' && p[1] != '\0')
694 expand_macro (p[1], expanded_exec, info, uris);
698 g_string_append_c (expanded_exec, *p);
703 /* No file substitutions */
704 if (uri_list == *uris && uri_list != NULL)
706 /* If there is no macro default to %f. This is also what KDE does */
707 g_string_append_c (expanded_exec, ' ');
708 expand_macro ('f', expanded_exec, info, uris);
711 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
712 g_string_free (expanded_exec, TRUE);
717 prepend_terminal_to_vector (int *argc,
724 char **term_argv = NULL;
729 g_return_val_if_fail (argc != NULL, FALSE);
730 g_return_val_if_fail (argv != NULL, FALSE);
738 /* compute size if not given */
741 for (i = 0; the_argv[i] != NULL; i++)
747 term_argv = g_new0 (char *, 3);
749 check = g_find_program_in_path ("gnome-terminal");
752 term_argv[0] = check;
753 /* Note that gnome-terminal takes -x and
754 * as -e in gnome-terminal is broken we use that. */
755 term_argv[1] = g_strdup ("-x");
760 check = g_find_program_in_path ("nxterm");
762 check = g_find_program_in_path ("color-xterm");
764 check = g_find_program_in_path ("rxvt");
766 check = g_find_program_in_path ("xterm");
768 check = g_find_program_in_path ("dtterm");
771 check = g_strdup ("xterm");
772 g_warning ("couldn't find a terminal, falling back to xterm");
774 term_argv[0] = check;
775 term_argv[1] = g_strdup ("-e");
778 real_argc = term_argc + *argc;
779 real_argv = g_new (char *, real_argc + 1);
781 for (i = 0; i < term_argc; i++)
782 real_argv[i] = term_argv[i];
784 for (j = 0; j < *argc; j++, i++)
785 real_argv[i] = (char *)the_argv[j];
793 /* we use g_free here as we sucked all the inner strings
794 * out from it into real_argv */
799 #endif /* G_OS_WIN32 */
802 /* '=' is the new '\0'.
803 * DO NOT CALL unless at least one string ends with '='
806 is_env (const char *a,
811 if (*a == 0 || *b == 0)
824 /* free with g_strfreev */
826 replace_env_var (char **old_environ,
828 const char *new_value)
830 int length, new_length;
831 int index, new_index;
835 /* do two things at once:
836 * - discover the length of the environment ('length')
837 * - find the location (if any) of the env var ('index')
840 for (length = 0; old_environ[length]; length++)
842 /* if we already have it in our environment, replace */
843 if (is_env (old_environ[length], env_var))
848 /* no current env var, no desired env value.
851 if (new_value == NULL && index == -1)
854 /* in all cases now, we will be using a modified environment.
855 * determine its length and allocated it.
858 * new_index = location to insert, if any
859 * new_length = length of the new array
860 * new_environ = the pointer array for the new environment
863 if (new_value == NULL && index >= 0)
865 /* in this case, we will be removing an entry */
866 new_length = length - 1;
869 else if (new_value != NULL && index < 0)
871 /* in this case, we will be adding an entry to the end */
872 new_length = length + 1;
876 /* in this case, we will be replacing the existing entry */
882 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
883 new_environ[new_length] = NULL;
885 /* now we do the copying.
886 * for each entry in the new environment, we decide what to do
890 for (new_i = 0; new_i < new_length; new_i++)
892 if (new_i == new_index)
894 /* insert our new item */
895 new_environ[new_i] = g_strconcat (env_var,
900 /* if we had an old entry, skip it now */
906 /* if this is the old DESKTOP_STARTUP_ID, skip it */
910 /* copy an old item */
911 new_environ[new_i] = g_strdup (old_environ[i]);
916 g_strfreev (old_environ);
922 uri_list_segment_to_files (GList *start,
929 while (start != NULL && start != end)
931 file = g_file_new_for_uri ((char *)start->data);
932 res = g_list_prepend (res, file);
936 return g_list_reverse (res);
939 #ifdef HAVE__NSGETENVIRON
940 #define environ (*_NSGetEnviron())
941 #elif !defined(G_OS_WIN32)
943 /* According to the Single Unix Specification, environ is not in
944 * * any system header, although unistd.h often declares it.
946 extern char **environ;
950 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
952 GAppLaunchContext *launch_context,
955 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
956 gboolean completed = FALSE;
958 GList *launched_files;
965 g_return_val_if_fail (appinfo != NULL, FALSE);
973 if (!expand_application_parameters (info, &uris,
974 &argc, &argv, error))
977 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
979 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
980 _("Unable to find terminal required for application"));
987 launched_files = uri_list_segment_to_files (old_uris, uris);
989 display = g_app_launch_context_get_display (launch_context,
994 if (info->startup_notify)
995 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
999 if (display || sn_id)
1003 envp = g_new0 (char *, 1);
1005 envp = g_strdupv (environ);
1009 envp = replace_env_var (envp,
1014 envp = replace_env_var (envp,
1015 "DESKTOP_STARTUP_ID",
1021 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1022 g_list_free (launched_files);
1025 if (!g_spawn_async (info->path, /* working directory */
1028 G_SPAWN_SEARCH_PATH /* flags */,
1029 NULL /* child_setup */,
1031 NULL /* child_pid */,
1036 g_app_launch_context_launch_failed (launch_context, sn_id);
1050 while (uris != NULL);
1062 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1064 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1066 return info->exec &&
1067 ((strstr (info->exec, "%u") != NULL) ||
1068 (strstr (info->exec, "%U") != NULL));
1072 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1074 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1076 return info->exec &&
1077 ((strstr (info->exec, "%f") != NULL) ||
1078 (strstr (info->exec, "%F") != NULL));
1082 g_desktop_app_info_launch (GAppInfo *appinfo,
1084 GAppLaunchContext *launch_context,
1094 uri = g_file_get_uri (files->data);
1095 uris = g_list_prepend (uris, uri);
1096 files = files->next;
1099 uris = g_list_reverse (uris);
1101 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1103 g_list_foreach (uris, (GFunc)g_free, NULL);
1109 G_LOCK_DEFINE_STATIC (g_desktop_env);
1110 static gchar *g_desktop_env = NULL;
1113 * g_desktop_app_info_set_desktop_env:
1114 * @desktop_env: a string specifying what desktop this is
1116 * Sets the name of the desktop that the application is running in.
1117 * This is used by g_app_info_should_show() to evaluate the
1118 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1119 * desktop entry fields.
1121 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1122 * Menu specification</ulink> recognizes the following:
1124 * <member>GNOME</member>
1125 * <member>KDE</member>
1126 * <member>ROX</member>
1127 * <member>XFCE</member>
1128 * <member>Old</member>
1131 * Should be called only once; subsequent calls are ignored.
1134 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1136 G_LOCK (g_desktop_env);
1138 g_desktop_env = g_strdup (desktop_env);
1139 G_UNLOCK (g_desktop_env);
1143 g_desktop_app_info_should_show (GAppInfo *appinfo)
1145 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1147 const gchar *desktop_env;
1150 if (info->nodisplay)
1153 G_LOCK (g_desktop_env);
1154 desktop_env = g_desktop_env;
1155 G_UNLOCK (g_desktop_env);
1157 if (info->only_show_in)
1159 if (desktop_env == NULL)
1163 for (i = 0; info->only_show_in[i] != NULL; i++)
1165 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1175 if (info->not_show_in && desktop_env)
1177 for (i = 0; info->not_show_in[i] != NULL; i++)
1179 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1193 ensure_dir (DirType type,
1196 char *path, *display_name;
1199 if (type == APP_DIR)
1200 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1202 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1205 if (g_mkdir_with_parents (path, 0700) == 0)
1209 display_name = g_filename_display_name (path);
1210 if (type == APP_DIR)
1211 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1212 _("Can't create user application configuration folder %s: %s"),
1213 display_name, g_strerror (errsv));
1215 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1216 _("Can't create user MIME configuration folder %s: %s"),
1217 display_name, g_strerror (errsv));
1219 g_free (display_name);
1226 update_mimeapps_list (const char *desktop_id,
1227 const char *content_type,
1228 gboolean add_as_default,
1229 gboolean add_non_default,
1233 char *dirname, *filename;
1235 gboolean load_succeeded, res;
1236 char **old_list, **list;
1237 GList *system_list, *l;
1238 gsize length, data_size;
1241 char **content_types;
1243 /* Don't add both at start and end */
1244 g_assert (!(add_as_default && add_non_default));
1246 dirname = ensure_dir (APP_DIR, error);
1250 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1253 key_file = g_key_file_new ();
1254 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1255 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1257 g_key_file_free (key_file);
1258 key_file = g_key_file_new ();
1263 content_types = g_new (char *, 2);
1264 content_types[0] = g_strdup (content_type);
1265 content_types[1] = NULL;
1269 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1272 for (k = 0; content_types && content_types[k]; k++)
1274 /* Add to the right place in the list */
1277 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1278 content_types[k], &length, NULL);
1280 list = g_new (char *, 1 + length + 1);
1284 list[i++] = g_strdup (desktop_id);
1287 for (j = 0; old_list[j] != NULL; j++)
1289 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1290 list[i++] = g_strdup (old_list[j]);
1291 else if (add_non_default)
1293 /* If adding as non-default, and its already in,
1294 don't change order of desktop ids */
1295 add_non_default = FALSE;
1296 list[i++] = g_strdup (old_list[j]);
1301 if (add_non_default)
1303 /* We're adding as non-default, and it wasn't already in the list,
1304 so we add at the end. But to avoid listing the app before the
1305 current system default (thus changing the default) we have to
1306 add the current list of (not yet listed) apps before it. */
1308 list[i] = NULL; /* Terminate current list so we can use it */
1309 system_list = get_all_desktop_entries_for_mime_type (content_type, list);
1311 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1313 for (l = system_list; l != NULL; l = l->next)
1315 list[i++] = l->data; /* no strdup, taking ownership */
1316 if (g_strcmp0 (l->data, desktop_id) == 0)
1317 add_non_default = FALSE;
1319 g_list_free (system_list);
1321 if (add_non_default)
1322 list[i++] = g_strdup (desktop_id);
1327 g_strfreev (old_list);
1329 if (list[0] == NULL || desktop_id == NULL)
1330 g_key_file_remove_key (key_file,
1331 ADDED_ASSOCIATIONS_GROUP,
1335 g_key_file_set_string_list (key_file,
1336 ADDED_ASSOCIATIONS_GROUP,
1338 (const char * const *)list, i);
1345 /* reuse the list from above */
1349 g_strfreev (content_types);
1350 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1353 for (k = 0; content_types && content_types[k]; k++)
1355 /* Remove from removed associations group (unless remove) */
1358 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1359 content_types[k], &length, NULL);
1361 list = g_new (char *, 1 + length + 1);
1365 list[i++] = g_strdup (desktop_id);
1368 for (j = 0; old_list[j] != NULL; j++)
1370 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1371 list[i++] = g_strdup (old_list[j]);
1376 g_strfreev (old_list);
1378 if (list[0] == NULL || desktop_id == NULL)
1379 g_key_file_remove_key (key_file,
1380 REMOVED_ASSOCIATIONS_GROUP,
1384 g_key_file_set_string_list (key_file,
1385 REMOVED_ASSOCIATIONS_GROUP,
1387 (const char * const *)list, i);
1392 g_strfreev (content_types);
1394 data = g_key_file_to_data (key_file, &data_size, error);
1395 g_key_file_free (key_file);
1397 res = g_file_set_contents (filename, data, data_size, error);
1399 mime_info_cache_reload (NULL);
1408 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1409 const char *content_type,
1412 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1414 if (!g_desktop_app_info_ensure_saved (info, error))
1417 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1421 update_program_done (GPid pid,
1425 /* Did the application exit correctly */
1426 if (WIFEXITED (status) &&
1427 WEXITSTATUS (status) == 0)
1429 /* Here we could clean out any caches in use */
1434 run_update_command (char *command,
1443 GError *error = NULL;
1446 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1448 if (g_spawn_async ("/", argv,
1450 G_SPAWN_SEARCH_PATH |
1451 G_SPAWN_STDOUT_TO_DEV_NULL |
1452 G_SPAWN_STDERR_TO_DEV_NULL |
1453 G_SPAWN_DO_NOT_REAP_CHILD,
1454 NULL, NULL, /* No setup function */
1457 g_child_watch_add (pid, update_program_done, NULL);
1460 /* If we get an error at this point, it's quite likely the user doesn't
1461 * have an installed copy of either 'update-mime-database' or
1462 * 'update-desktop-database'. I don't think we want to popup an error
1463 * dialog at this point, so we just do a g_warning to give the user a
1464 * chance of debugging it.
1466 g_warning ("%s", error->message);
1473 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1474 const char *extension,
1477 char *filename, *basename, *mimetype;
1481 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1484 dirname = ensure_dir (MIMETYPE_DIR, error);
1488 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1489 filename = g_build_filename (dirname, basename, NULL);
1493 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1495 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1500 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1501 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1502 " <mime-type type=\"%s\">\n"
1503 " <comment>%s document</comment>\n"
1504 " <glob pattern=\"*.%s\"/>\n"
1506 "</mime-info>\n", mimetype, extension, extension);
1508 g_file_set_contents (filename, contents, -1, NULL);
1511 run_update_command ("update-mime-database", "mime");
1515 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1525 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1526 const char *content_type,
1529 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1531 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1534 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1538 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1544 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1545 const char *content_type,
1548 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1550 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1553 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1557 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1563 char *data, *desktop_id;
1568 if (info->filename != NULL)
1571 /* This is only used for object created with
1572 * g_app_info_create_from_commandline. All other
1573 * object should have a filename
1576 dirname = ensure_dir (APP_DIR, error);
1580 key_file = g_key_file_new ();
1582 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1583 "Encoding", "UTF-8");
1584 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1585 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1586 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1587 G_KEY_FILE_DESKTOP_KEY_TYPE,
1588 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1590 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1591 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1593 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1594 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1596 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1597 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1599 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1600 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1602 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1603 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1605 data = g_key_file_to_data (key_file, &data_size, NULL);
1606 g_key_file_free (key_file);
1608 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1609 filename = g_build_filename (dirname, desktop_id, NULL);
1610 g_free (desktop_id);
1613 fd = g_mkstemp (filename);
1618 display_name = g_filename_display_name (filename);
1619 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1620 _("Can't create user desktop file %s"), display_name);
1621 g_free (display_name);
1627 desktop_id = g_path_get_basename (filename);
1631 res = g_file_set_contents (filename, data, data_size, error);
1634 g_free (desktop_id);
1639 info->filename = filename;
1640 info->desktop_id = desktop_id;
1642 run_update_command ("update-desktop-database", "applications");
1648 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1650 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1654 if (strstr (info->filename, "/userapp-"))
1655 return g_access (info->filename, W_OK) == 0;
1662 g_desktop_app_info_delete (GAppInfo *appinfo)
1664 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1668 if (g_remove (info->filename) == 0)
1670 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1672 g_free (info->filename);
1673 info->filename = NULL;
1674 g_free (info->desktop_id);
1675 info->desktop_id = NULL;
1685 * g_app_info_create_from_commandline:
1686 * @commandline: the commandline to use
1687 * @application_name: the application name, or %NULL to use @commandline
1688 * @flags: flags that can specify details of the created #GAppInfo
1689 * @error: a #GError location to store the error occuring, %NULL to ignore.
1691 * Creates a new #GAppInfo from the given information.
1693 * Returns: new #GAppInfo for given command.
1696 g_app_info_create_from_commandline (const char *commandline,
1697 const char *application_name,
1698 GAppInfoCreateFlags flags,
1703 GDesktopAppInfo *info;
1705 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1707 info->filename = NULL;
1708 info->desktop_id = NULL;
1710 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1711 info->startup_notify = FALSE;
1712 info->hidden = FALSE;
1713 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1714 info->exec = g_strconcat (commandline, " %u", NULL);
1716 info->exec = g_strconcat (commandline, " %f", NULL);
1717 info->nodisplay = TRUE;
1718 info->binary = binary_from_exec (info->exec);
1720 if (application_name)
1721 info->name = g_strdup (application_name);
1724 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1725 split = g_strsplit (commandline, " ", 2);
1726 basename = g_path_get_basename (split[0]);
1728 info->name = basename;
1729 if (info->name == NULL)
1730 info->name = g_strdup ("custom");
1732 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1734 return G_APP_INFO (info);
1738 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1740 iface->dup = g_desktop_app_info_dup;
1741 iface->equal = g_desktop_app_info_equal;
1742 iface->get_id = g_desktop_app_info_get_id;
1743 iface->get_name = g_desktop_app_info_get_name;
1744 iface->get_description = g_desktop_app_info_get_description;
1745 iface->get_executable = g_desktop_app_info_get_executable;
1746 iface->get_icon = g_desktop_app_info_get_icon;
1747 iface->launch = g_desktop_app_info_launch;
1748 iface->supports_uris = g_desktop_app_info_supports_uris;
1749 iface->supports_files = g_desktop_app_info_supports_files;
1750 iface->launch_uris = g_desktop_app_info_launch_uris;
1751 iface->should_show = g_desktop_app_info_should_show;
1752 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1753 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1754 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1755 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1756 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1757 iface->can_delete = g_desktop_app_info_can_delete;
1758 iface->do_delete = g_desktop_app_info_delete;
1759 iface->get_commandline = g_desktop_app_info_get_commandline;
1763 app_info_in_list (GAppInfo *info,
1766 while (list != NULL)
1768 if (g_app_info_equal (info, list->data))
1777 * g_app_info_get_all_for_type:
1778 * @content_type: the content type to find a #GAppInfo for
1780 * Gets a list of all #GAppInfo s for a given content type.
1782 * Returns: #GList of #GAppInfo s for given @content_type
1783 * or %NULL on error.
1786 g_app_info_get_all_for_type (const char *content_type)
1788 GList *desktop_entries, *l;
1790 GDesktopAppInfo *info;
1792 g_return_val_if_fail (content_type != NULL, NULL);
1794 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1797 for (l = desktop_entries; l != NULL; l = l->next)
1799 char *desktop_entry = l->data;
1801 info = g_desktop_app_info_new (desktop_entry);
1804 if (app_info_in_list (G_APP_INFO (info), infos))
1805 g_object_unref (info);
1807 infos = g_list_prepend (infos, info);
1809 g_free (desktop_entry);
1812 g_list_free (desktop_entries);
1814 return g_list_reverse (infos);
1818 * g_app_info_reset_type_associations:
1819 * @content_type: a content type
1821 * Removes all changes to the type associations done by
1822 * g_app_info_set_as_default_for_type(),
1823 * g_app_info_set_as_default_for_extension(),
1824 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1829 g_app_info_reset_type_associations (const char *content_type)
1831 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1835 * g_app_info_get_default_for_type:
1836 * @content_type: the content type to find a #GAppInfo for
1837 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1840 * Gets the #GAppInfo that correspond to a given content type.
1842 * Returns: #GAppInfo for given @content_type or %NULL on error.
1845 g_app_info_get_default_for_type (const char *content_type,
1846 gboolean must_support_uris)
1848 GList *desktop_entries, *l;
1851 g_return_val_if_fail (content_type != NULL, NULL);
1853 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1856 for (l = desktop_entries; l != NULL; l = l->next)
1858 char *desktop_entry = l->data;
1860 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1863 if (must_support_uris && !g_app_info_supports_uris (info))
1865 g_object_unref (info);
1873 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1874 g_list_free (desktop_entries);
1880 * g_app_info_get_default_for_uri_scheme:
1881 * @uri_scheme: a string containing a URI scheme.
1883 * Gets the default application for launching applications
1884 * using this URI scheme. A URI scheme is the initial part
1885 * of the URI, up to but not including the ':', e.g. "http",
1888 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1891 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1893 static gsize lookup = 0;
1895 if (g_once_init_enter (&lookup))
1897 gsize setup_value = 1;
1898 GDesktopAppInfoLookup *lookup_instance;
1899 const char *use_this;
1900 GIOExtensionPoint *ep;
1901 GIOExtension *extension;
1904 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1906 /* Ensure vfs in modules loaded */
1907 _g_io_modules_ensure_loaded ();
1909 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1911 lookup_instance = NULL;
1914 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1916 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1919 if (lookup_instance == NULL)
1921 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1923 extension = l->data;
1924 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1925 if (lookup_instance != NULL)
1930 if (lookup_instance != NULL)
1931 setup_value = (gsize)lookup_instance;
1933 g_once_init_leave (&lookup, setup_value);
1939 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
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);
1970 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1972 g_object_unref (appinfo);
1977 if (appinfo || hidden)
1979 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1983 /* Reuse instead of strdup here */
1984 appinfo->desktop_id = desktop_id;
1989 g_free (desktop_id);
1993 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1995 subprefix = g_strconcat (prefix, basename, "-", NULL);
1996 get_apps_from_dir (apps, filename, subprefix);
2008 * g_app_info_get_all:
2010 * Gets a list of all of the applications currently registered
2013 * For desktop files, this includes applications that have
2014 * <literal>NoDisplay=true</literal> set or are excluded from
2015 * display by means of <literal>OnlyShowIn</literal> or
2016 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2017 * The returned list does not include applications which have
2018 * the <literal>Hidden</literal> key set.
2020 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2023 g_app_info_get_all (void)
2025 const char * const *dirs;
2027 GHashTableIter iter;
2032 dirs = get_applications_search_path ();
2034 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2038 for (i = 0; dirs[i] != NULL; i++)
2039 get_apps_from_dir (apps, dirs[i], "");
2043 g_hash_table_iter_init (&iter, apps);
2044 while (g_hash_table_iter_next (&iter, NULL, &value))
2047 infos = g_list_prepend (infos, value);
2050 g_hash_table_destroy (apps);
2052 return g_list_reverse (infos);
2055 /* Cacheing of mimeinfo.cache and defaults.list files */
2059 GHashTable *mime_info_cache_map;
2060 GHashTable *defaults_list_map;
2061 GHashTable *mimeapps_list_added_map;
2062 GHashTable *mimeapps_list_removed_map;
2063 time_t mime_info_cache_timestamp;
2064 time_t defaults_list_timestamp;
2065 time_t mimeapps_list_timestamp;
2069 GList *dirs; /* mimeinfo.cache and defaults.list */
2070 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2071 time_t last_stat_time;
2072 guint should_ping_mime_monitor : 1;
2075 static MimeInfoCache *mime_info_cache = NULL;
2076 G_LOCK_DEFINE_STATIC (mime_info_cache);
2078 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2079 const char *mime_type,
2080 char **new_desktop_file_ids);
2082 static MimeInfoCache * mime_info_cache_new (void);
2085 destroy_info_cache_value (gpointer key,
2089 g_list_foreach (value, (GFunc)g_free, NULL);
2090 g_list_free (value);
2094 destroy_info_cache_map (GHashTable *info_cache_map)
2096 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2097 g_hash_table_destroy (info_cache_map);
2101 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2102 const char *cache_file,
2108 filename = g_build_filename (dir->path, cache_file, NULL);
2110 if (g_stat (filename, &buf) < 0)
2117 if (buf.st_mtime != *timestamp)
2123 /* Call with lock held */
2125 remove_all (gpointer key,
2134 mime_info_cache_blow_global_cache (void)
2136 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2141 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2145 gchar *filename, **mime_types;
2152 if (dir->mime_info_cache_map != NULL &&
2153 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2154 &dir->mime_info_cache_timestamp))
2157 if (dir->mime_info_cache_map != NULL)
2158 destroy_info_cache_map (dir->mime_info_cache_map);
2160 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2161 (GDestroyNotify) g_free,
2164 key_file = g_key_file_new ();
2166 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2168 if (g_stat (filename, &buf) < 0)
2171 if (dir->mime_info_cache_timestamp > 0)
2172 mime_info_cache->should_ping_mime_monitor = TRUE;
2174 dir->mime_info_cache_timestamp = buf.st_mtime;
2176 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2181 if (load_error != NULL)
2184 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2187 if (load_error != NULL)
2190 for (i = 0; mime_types[i] != NULL; i++)
2192 gchar **desktop_file_ids;
2193 char *unaliased_type;
2194 desktop_file_ids = g_key_file_get_string_list (key_file,
2200 if (desktop_file_ids == NULL)
2203 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2204 mime_info_cache_dir_add_desktop_entries (dir,
2207 g_free (unaliased_type);
2209 g_strfreev (desktop_file_ids);
2212 g_strfreev (mime_types);
2213 g_key_file_free (key_file);
2218 g_key_file_free (key_file);
2220 if (mime_types != NULL)
2221 g_strfreev (mime_types);
2224 g_error_free (load_error);
2228 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2232 gchar *filename, **mime_types;
2233 char *unaliased_type;
2234 char **desktop_file_ids;
2241 if (dir->defaults_list_map != NULL &&
2242 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2243 &dir->defaults_list_timestamp))
2246 if (dir->defaults_list_map != NULL)
2247 g_hash_table_destroy (dir->defaults_list_map);
2248 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2249 g_free, (GDestroyNotify)g_strfreev);
2252 key_file = g_key_file_new ();
2254 filename = g_build_filename (dir->path, "defaults.list", NULL);
2255 if (g_stat (filename, &buf) < 0)
2258 if (dir->defaults_list_timestamp > 0)
2259 mime_info_cache->should_ping_mime_monitor = TRUE;
2261 dir->defaults_list_timestamp = buf.st_mtime;
2263 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2267 if (load_error != NULL)
2270 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2272 if (mime_types != NULL)
2274 for (i = 0; mime_types[i] != NULL; i++)
2276 desktop_file_ids = g_key_file_get_string_list (key_file,
2277 DEFAULT_APPLICATIONS_GROUP,
2281 if (desktop_file_ids == NULL)
2284 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2285 g_hash_table_replace (dir->defaults_list_map,
2290 g_strfreev (mime_types);
2293 g_key_file_free (key_file);
2298 g_key_file_free (key_file);
2300 if (mime_types != NULL)
2301 g_strfreev (mime_types);
2304 g_error_free (load_error);
2308 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2312 gchar *filename, **mime_types;
2313 char *unaliased_type;
2314 char **desktop_file_ids;
2321 if (dir->mimeapps_list_added_map != NULL &&
2322 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2323 &dir->mimeapps_list_timestamp))
2326 if (dir->mimeapps_list_added_map != NULL)
2327 g_hash_table_destroy (dir->mimeapps_list_added_map);
2328 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2329 g_free, (GDestroyNotify)g_strfreev);
2331 if (dir->mimeapps_list_removed_map != NULL)
2332 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2333 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2334 g_free, (GDestroyNotify)g_strfreev);
2336 key_file = g_key_file_new ();
2338 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2339 if (g_stat (filename, &buf) < 0)
2342 if (dir->mimeapps_list_timestamp > 0)
2343 mime_info_cache->should_ping_mime_monitor = TRUE;
2345 dir->mimeapps_list_timestamp = buf.st_mtime;
2347 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2351 if (load_error != NULL)
2354 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2356 if (mime_types != NULL)
2358 for (i = 0; mime_types[i] != NULL; i++)
2360 desktop_file_ids = g_key_file_get_string_list (key_file,
2361 ADDED_ASSOCIATIONS_GROUP,
2365 if (desktop_file_ids == NULL)
2368 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2369 g_hash_table_replace (dir->mimeapps_list_added_map,
2374 g_strfreev (mime_types);
2377 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2379 if (mime_types != NULL)
2381 for (i = 0; mime_types[i] != NULL; i++)
2383 desktop_file_ids = g_key_file_get_string_list (key_file,
2384 REMOVED_ASSOCIATIONS_GROUP,
2388 if (desktop_file_ids == NULL)
2391 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2392 g_hash_table_replace (dir->mimeapps_list_removed_map,
2397 g_strfreev (mime_types);
2400 g_key_file_free (key_file);
2405 g_key_file_free (key_file);
2407 if (mime_types != NULL)
2408 g_strfreev (mime_types);
2411 g_error_free (load_error);
2414 static MimeInfoCacheDir *
2415 mime_info_cache_dir_new (const char *path)
2417 MimeInfoCacheDir *dir;
2419 dir = g_new0 (MimeInfoCacheDir, 1);
2420 dir->path = g_strdup (path);
2426 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2431 if (dir->mime_info_cache_map != NULL)
2433 destroy_info_cache_map (dir->mime_info_cache_map);
2434 dir->mime_info_cache_map = NULL;
2438 if (dir->defaults_list_map != NULL)
2440 g_hash_table_destroy (dir->defaults_list_map);
2441 dir->defaults_list_map = NULL;
2444 if (dir->mimeapps_list_added_map != NULL)
2446 g_hash_table_destroy (dir->mimeapps_list_added_map);
2447 dir->mimeapps_list_added_map = NULL;
2450 if (dir->mimeapps_list_removed_map != NULL)
2452 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2453 dir->mimeapps_list_removed_map = NULL;
2460 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2461 const char *mime_type,
2462 char **new_desktop_file_ids)
2464 GList *desktop_file_ids;
2467 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2470 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2472 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2473 desktop_file_ids = g_list_append (desktop_file_ids,
2474 g_strdup (new_desktop_file_ids[i]));
2477 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2481 mime_info_cache_init_dir_lists (void)
2483 const char * const *dirs;
2486 mime_info_cache = mime_info_cache_new ();
2488 dirs = get_applications_search_path ();
2490 for (i = 0; dirs[i] != NULL; i++)
2492 MimeInfoCacheDir *dir;
2494 dir = mime_info_cache_dir_new (dirs[i]);
2498 mime_info_cache_dir_init (dir);
2499 mime_info_cache_dir_init_defaults_list (dir);
2500 mime_info_cache_dir_init_mimeapps_list (dir);
2502 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2508 mime_info_cache_update_dir_lists (void)
2512 tmp = mime_info_cache->dirs;
2516 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2518 /* No need to do this if we had file monitors... */
2519 mime_info_cache_blow_global_cache ();
2520 mime_info_cache_dir_init (dir);
2521 mime_info_cache_dir_init_defaults_list (dir);
2522 mime_info_cache_dir_init_mimeapps_list (dir);
2529 mime_info_cache_init (void)
2531 G_LOCK (mime_info_cache);
2532 if (mime_info_cache == NULL)
2533 mime_info_cache_init_dir_lists ();
2539 if (now >= mime_info_cache->last_stat_time + 10)
2541 mime_info_cache_update_dir_lists ();
2542 mime_info_cache->last_stat_time = now;
2546 if (mime_info_cache->should_ping_mime_monitor)
2548 /* g_idle_add (emit_mime_changed, NULL); */
2549 mime_info_cache->should_ping_mime_monitor = FALSE;
2552 G_UNLOCK (mime_info_cache);
2555 static MimeInfoCache *
2556 mime_info_cache_new (void)
2558 MimeInfoCache *cache;
2560 cache = g_new0 (MimeInfoCache, 1);
2562 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2563 (GDestroyNotify) g_free,
2564 (GDestroyNotify) g_free);
2569 mime_info_cache_free (MimeInfoCache *cache)
2574 g_list_foreach (cache->dirs,
2575 (GFunc) mime_info_cache_dir_free,
2577 g_list_free (cache->dirs);
2578 g_hash_table_destroy (cache->global_defaults_cache);
2583 * mime_info_cache_reload:
2584 * @dir: directory path which needs reloading.
2586 * Reload the mime information for the @dir.
2589 mime_info_cache_reload (const char *dir)
2591 /* FIXME: just reload the dir that needs reloading,
2592 * don't blow the whole cache
2594 if (mime_info_cache != NULL)
2596 G_LOCK (mime_info_cache);
2597 mime_info_cache_free (mime_info_cache);
2598 mime_info_cache = NULL;
2599 G_UNLOCK (mime_info_cache);
2604 append_desktop_entry (GList *list,
2605 const char *desktop_entry,
2606 GList *removed_entries)
2608 /* Add if not already in list, and valid */
2609 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2610 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2611 list = g_list_prepend (list, g_strdup (desktop_entry));
2617 * get_all_desktop_entries_for_mime_type:
2618 * @mime_type: a mime type.
2619 * @except: NULL or a strv list
2621 * Returns all the desktop ids for @mime_type. The desktop files
2622 * are listed in an order so that default applications are listed before
2623 * non-default ones, and handlers for inherited mimetypes are listed
2624 * after the base ones.
2626 * Optionally doesn't list the desktop ids given in the @except
2628 * Return value: a #GList containing the desktop ids which claim
2629 * to handle @mime_type.
2632 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2633 const char **except)
2635 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2636 MimeInfoCacheDir *dir;
2639 char **default_entries;
2640 char **removed_associations;
2645 mime_info_cache_init ();
2647 /* collect all ancestors */
2648 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2649 array = g_ptr_array_new ();
2650 for (i = 0; mime_types[i]; i++)
2651 g_ptr_array_add (array, mime_types[i]);
2652 g_free (mime_types);
2653 for (i = 0; i < array->len; i++)
2655 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2656 for (j = 0; anc[j]; j++)
2658 for (k = 0; k < array->len; k++)
2660 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2663 if (k == array->len) /* not found */
2664 g_ptr_array_add (array, g_strdup (anc[j]));
2668 g_ptr_array_add (array, NULL);
2669 mime_types = (char **)g_ptr_array_free (array, FALSE);
2671 G_LOCK (mime_info_cache);
2673 removed_entries = NULL;
2674 desktop_entries = NULL;
2676 for (i = 0; except != NULL && except[i] != NULL; i++)
2677 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2679 for (i = 0; mime_types[i] != NULL; i++)
2681 mime_type = mime_types[i];
2683 /* Go through all apps listed as defaults */
2684 for (dir_list = mime_info_cache->dirs;
2686 dir_list = dir_list->next)
2688 dir = dir_list->data;
2690 /* First added associations from mimeapps.list */
2691 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2692 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2693 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2695 /* Then removed associations from mimeapps.list */
2696 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2697 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2698 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2700 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2701 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2702 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2703 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2706 /* Go through all entries that support the mimetype */
2707 for (dir_list = mime_info_cache->dirs;
2709 dir_list = dir_list->next)
2711 dir = dir_list->data;
2713 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2714 for (tmp = list; tmp != NULL; tmp = tmp->next)
2715 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2719 G_UNLOCK (mime_info_cache);
2721 g_strfreev (mime_types);
2723 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2724 g_list_free (removed_entries);
2726 desktop_entries = g_list_reverse (desktop_entries);
2728 return desktop_entries;
2731 /* GDesktopAppInfoLookup interface: */
2733 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2734 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2735 gpointer class_data);
2738 g_desktop_app_info_lookup_get_type (void)
2740 static volatile gsize g_define_type_id__volatile = 0;
2742 if (g_once_init_enter (&g_define_type_id__volatile))
2744 const GTypeInfo desktop_app_info_lookup_info =
2746 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2747 g_desktop_app_info_lookup_base_init, /* base_init */
2748 NULL, /* base_finalize */
2749 g_desktop_app_info_lookup_class_init,
2750 NULL, /* class_finalize */
2751 NULL, /* class_data */
2753 0, /* n_preallocs */
2756 GType g_define_type_id =
2757 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2758 &desktop_app_info_lookup_info, 0);
2760 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2762 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2765 return g_define_type_id__volatile;
2769 g_desktop_app_info_lookup_class_init (gpointer g_class,
2770 gpointer class_data)
2775 g_desktop_app_info_lookup_base_init (gpointer g_class)
2780 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2781 * @lookup: a #GDesktopAppInfoLookup
2782 * @uri_scheme: a string containing a URI scheme.
2784 * Gets the default application for launching applications
2785 * using this URI scheme for a particular GDesktopAppInfoLookup
2788 * The GDesktopAppInfoLookup interface and this function is used
2789 * to implement g_app_info_get_default_for_uri_scheme() backends
2790 * in a GIO module. There is no reason for applications to use it
2791 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2793 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2796 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2797 const char *uri_scheme)
2799 GDesktopAppInfoLookupIface *iface;
2801 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2803 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2805 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2808 #define __G_DESKTOP_APP_INFO_C__
2809 #include "gioaliasdef.c"