Add GAppInfoMonitor
[platform/upstream/glib.git] / gio / gdesktopappinfo.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  * Copyright © 2007 Ryan Lortie
5  *
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.
10  *
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.
15  *
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.
20  *
21  * Author: Alexander Larsson <alexl@redhat.com>
22  *         Ryan Lortie <desrt@desrt.ca>
23  */
24
25 /* Prelude {{{1 */
26
27 #include "config.h"
28
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #ifdef HAVE_CRT_EXTERNS_H
34 #include <crt_externs.h>
35 #endif
36
37 #include "gcontenttypeprivate.h"
38 #include "gdesktopappinfo.h"
39 #ifdef G_OS_UNIX
40 #include "glib-unix.h"
41 #endif
42 #include "gfile.h"
43 #include "gioerror.h"
44 #include "gthemedicon.h"
45 #include "gfileicon.h"
46 #include <glib/gstdio.h>
47 #include "glibintl.h"
48 #include "giomodule-priv.h"
49 #include "gappinfo.h"
50 #include "gappinfoprivate.h"
51 #include "glocaldirectorymonitor.h"
52
53
54 /**
55  * SECTION:gdesktopappinfo
56  * @title: GDesktopAppInfo
57  * @short_description: Application information from desktop files
58  * @include: gio/gdesktopappinfo.h
59  *
60  * #GDesktopAppInfo is an implementation of #GAppInfo based on
61  * desktop files.
62  *
63  * Note that <filename>&lt;gio/gdesktopappinfo.h&gt;</filename> belongs to
64  * the UNIX-specific GIO interfaces, thus you have to use the
65  * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
66  */
67
68 #define DEFAULT_APPLICATIONS_GROUP  "Default Applications"
69 #define ADDED_ASSOCIATIONS_GROUP    "Added Associations"
70 #define REMOVED_ASSOCIATIONS_GROUP  "Removed Associations"
71 #define MIME_CACHE_GROUP            "MIME Cache"
72 #define GENERIC_NAME_KEY            "GenericName"
73 #define FULL_NAME_KEY               "X-GNOME-FullName"
74 #define KEYWORDS_KEY                "Keywords"
75 #define STARTUP_WM_CLASS_KEY        "StartupWMClass"
76
77 enum {
78   PROP_0,
79   PROP_FILENAME
80 };
81
82 static void     g_desktop_app_info_iface_init         (GAppInfoIface    *iface);
83 static GList *  get_all_desktop_entries_for_mime_type (const char       *base_mime_type,
84                                                        const char      **except,
85                                                        gboolean          include_fallback,
86                                                        char            **explicit_default);
87 static void     mime_info_cache_reload                (const char       *dir);
88 static gboolean g_desktop_app_info_ensure_saved       (GDesktopAppInfo  *info,
89                                                        GError          **error);
90
91 /**
92  * GDesktopAppInfo:
93  *
94  * Information about an installed application from a desktop file.
95  */
96 struct _GDesktopAppInfo
97 {
98   GObject parent_instance;
99
100   char *desktop_id;
101   char *filename;
102   char *app_id;
103
104   GKeyFile *keyfile;
105
106   char *name;
107   char *generic_name;
108   char *fullname;
109   char *comment;
110   char *icon_name;
111   GIcon *icon;
112   char **keywords;
113   char **only_show_in;
114   char **not_show_in;
115   char *try_exec;
116   char *exec;
117   char *binary;
118   char *path;
119   char *categories;
120   char *startup_wm_class;
121   char **mime_types;
122   char **actions;
123
124   guint nodisplay       : 1;
125   guint hidden          : 1;
126   guint terminal        : 1;
127   guint startup_notify  : 1;
128   guint no_fuse         : 1;
129 };
130
131 typedef enum {
132   UPDATE_MIME_NONE = 1 << 0,
133   UPDATE_MIME_SET_DEFAULT = 1 << 1,
134   UPDATE_MIME_SET_NON_DEFAULT = 1 << 2,
135   UPDATE_MIME_REMOVE = 1 << 3,
136   UPDATE_MIME_SET_LAST_USED = 1 << 4,
137 } UpdateMimeFlags;
138
139 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
140                          G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO, g_desktop_app_info_iface_init))
141
142 G_LOCK_DEFINE_STATIC (g_desktop_env);
143 static gchar *g_desktop_env = NULL;
144
145 /* DesktopFileDir implementation {{{1 */
146
147 typedef struct
148 {
149   gchar                      *path;
150   GLocalDirectoryMonitor     *monitor;
151   GHashTable                 *app_names;
152   gboolean                    is_setup;
153 } DesktopFileDir;
154
155 static DesktopFileDir *desktop_file_dirs;
156 static guint           n_desktop_file_dirs;
157 static GMutex          desktop_file_dir_lock;
158
159 /* Monitor 'changed' signal handler {{{2 */
160 static void desktop_file_dir_reset (DesktopFileDir *dir);
161
162 static void
163 desktop_file_dir_changed (GFileMonitor      *monitor,
164                           GFile             *file,
165                           GFile             *other_file,
166                           GFileMonitorEvent  event_type,
167                           gpointer           user_data)
168 {
169   DesktopFileDir *dir = user_data;
170
171   /* We are not interested in receiving notifications forever just
172    * because someone asked about one desktop file once.
173    *
174    * After we receive the first notification, reset the dir, destroying
175    * the monitor.  We will take this as a hint, next time that we are
176    * asked, that we need to check if everything is up to date.
177    */
178   g_mutex_lock (&desktop_file_dir_lock);
179
180   desktop_file_dir_reset (dir);
181
182   g_mutex_unlock (&desktop_file_dir_lock);
183
184   /* Notify anyone else who may be interested */
185   g_app_info_monitor_fire ();
186 }
187
188 /* Internal utility functions {{{2 */
189
190 /*< internal >
191  * desktop_file_dir_app_name_is_masked:
192  * @dir: a #DesktopFileDir
193  * @app_name: an application ID
194  *
195  * Checks if @app_name is masked for @dir.
196  *
197  * An application is masked if a similarly-named desktop file exists in
198  * a desktop file directory with higher precedence.  Masked desktop
199  * files should be ignored.
200  */
201 static gboolean
202 desktop_file_dir_app_name_is_masked (DesktopFileDir *dir,
203                                      const gchar    *app_name)
204 {
205   while (dir > desktop_file_dirs)
206     {
207       dir--;
208
209       if (dir->app_names && g_hash_table_contains (dir->app_names, app_name))
210         return TRUE;
211     }
212
213   return FALSE;
214 }
215
216 /*< internal >
217  * add_to_table_if_appropriate:
218  * @apps: a string to GDesktopAppInfo hash table
219  * @app_name: the name of the application
220  * @info: a #GDesktopAppInfo, or NULL
221  *
222  * If @info is non-%NULL and non-hidden, then add it to @apps, using
223  * @app_name as a key.
224  *
225  * If @info is non-%NULL then this function will consume the passed-in
226  * reference.
227  */
228 static void
229 add_to_table_if_appropriate (GHashTable      *apps,
230                              const gchar     *app_name,
231                              GDesktopAppInfo *info)
232 {
233   if (!info)
234     return;
235
236   if (info->hidden)
237     {
238       g_object_unref (info);
239       return;
240     }
241
242   g_free (info->desktop_id);
243   info->desktop_id = g_strdup (app_name);
244
245   g_hash_table_insert (apps, g_strdup (info->desktop_id), info);
246 }
247
248 /* Support for unindexed DesktopFileDirs {{{2 */
249 static void
250 get_apps_from_dir (GHashTable **apps,
251                    const char  *dirname,
252                    const char  *prefix)
253 {
254   const char *basename;
255   GDir *dir;
256
257   dir = g_dir_open (dirname, 0, NULL);
258
259   if (dir == NULL)
260     return;
261
262   while ((basename = g_dir_read_name (dir)) != NULL)
263     {
264       gchar *filename;
265
266       filename = g_build_filename (dirname, basename, NULL);
267
268       if (g_str_has_suffix (basename, ".desktop"))
269         {
270           gchar *app_name;
271
272           app_name = g_strconcat (prefix, basename, NULL);
273
274           if (*apps == NULL)
275             *apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
276
277           g_hash_table_insert (*apps, app_name, g_strdup (filename));
278         }
279       else if (g_file_test (filename, G_FILE_TEST_IS_DIR))
280         {
281           gchar *subprefix;
282
283           subprefix = g_strconcat (prefix, basename, "-", NULL);
284           get_apps_from_dir (apps, filename, subprefix);
285           g_free (subprefix);
286         }
287
288       g_free (filename);
289     }
290
291   g_dir_close (dir);
292 }
293
294 static void
295 desktop_file_dir_unindexed_init (DesktopFileDir *dir)
296 {
297   get_apps_from_dir (&dir->app_names, dir->path, "");
298 }
299
300 static GDesktopAppInfo *
301 desktop_file_dir_unindexed_get_app (DesktopFileDir *dir,
302                                     const gchar    *desktop_id)
303 {
304   const gchar *filename;
305
306   filename = g_hash_table_lookup (dir->app_names, desktop_id);
307
308   if (!filename)
309     return NULL;
310
311   return g_desktop_app_info_new_from_filename (filename);
312 }
313
314 static void
315 desktop_file_dir_unindexed_get_all (DesktopFileDir *dir,
316                                     GHashTable     *apps)
317 {
318   GHashTableIter iter;
319   gpointer app_name;
320   gpointer filename;
321
322   if (dir->app_names == NULL)
323     return;
324
325   g_hash_table_iter_init (&iter, dir->app_names);
326   while (g_hash_table_iter_next (&iter, &app_name, &filename))
327     {
328       if (desktop_file_dir_app_name_is_masked (dir, app_name))
329         continue;
330
331       add_to_table_if_appropriate (apps, app_name, g_desktop_app_info_new_from_filename (filename));
332     }
333 }
334
335 /* DesktopFileDir "API" {{{2 */
336
337 /*< internal >
338  * desktop_file_dir_create:
339  * @array: the #GArray to add a new item to
340  * @data_dir: an XDG_DATA_DIR
341  *
342  * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
343  * to @array.
344  */
345 static void
346 desktop_file_dir_create (GArray      *array,
347                          const gchar *data_dir)
348 {
349   DesktopFileDir dir = { 0, };
350
351   dir.path = g_build_filename (data_dir, "applications", NULL);
352
353   g_array_append_val (array, dir);
354 }
355
356 /*< internal >
357  * desktop_file_dir_reset:
358  * @dir: a #DesktopFileDir
359  *
360  * Cleans up @dir, releasing most resources that it was using.
361  */
362 static void
363 desktop_file_dir_reset (DesktopFileDir *dir)
364 {
365   if (dir->monitor)
366     {
367       g_signal_handlers_disconnect_by_func (dir->monitor, desktop_file_dir_changed, dir);
368       g_object_unref (dir->monitor);
369       dir->monitor = NULL;
370     }
371
372   if (dir->app_names)
373     {
374       g_hash_table_unref (dir->app_names);
375       dir->app_names = NULL;
376     }
377
378   dir->is_setup = FALSE;
379 }
380
381 /*< internal >
382  * desktop_file_dir_init:
383  * @dir: a #DesktopFileDir
384  *
385  * Does initial setup for @dir
386  *
387  * You should only call this if @dir is not already setup.
388  */
389 static void
390 desktop_file_dir_init (DesktopFileDir *dir)
391 {
392   g_assert (!dir->is_setup);
393
394   g_assert (!dir->monitor);
395   dir->monitor = g_local_directory_monitor_new_in_worker (dir->path, G_FILE_MONITOR_NONE, NULL);
396
397   if (dir->monitor)
398     {
399       g_signal_connect (dir->monitor, "changed", G_CALLBACK (desktop_file_dir_changed), dir);
400       g_local_directory_monitor_start (dir->monitor);
401     }
402
403   desktop_file_dir_unindexed_init (dir);
404
405   dir->is_setup = TRUE;
406 }
407
408 /*< internal >
409  * desktop_file_dir_get_app:
410  * @dir: a DesktopFileDir
411  * @desktop_id: the desktop ID to load
412  *
413  * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
414  * within @dir, even if it is hidden.
415  *
416  * This function does not check if @desktop_id would be masked by a
417  * directory with higher precedence.  The caller must do so.
418  */
419 static GDesktopAppInfo *
420 desktop_file_dir_get_app (DesktopFileDir *dir,
421                           const gchar    *desktop_id)
422 {
423   if (!dir->app_names)
424     return NULL;
425
426   return desktop_file_dir_unindexed_get_app (dir, desktop_id);
427 }
428
429 /*< internal >
430  * desktop_file_dir_get_all:
431  * @dir: a DesktopFileDir
432  * @apps: a #GHashTable<string, GDesktopAppInfo>
433  *
434  * Loads all desktop files in @dir and adds them to @apps, careful to
435  * ensure we don't add any files masked by a similarly-named file in a
436  * higher-precedence directory.
437  */
438 static void
439 desktop_file_dir_get_all (DesktopFileDir *dir,
440                           GHashTable     *apps)
441 {
442   desktop_file_dir_unindexed_get_all (dir, apps);
443 }
444
445 /* Lock/unlock and global setup API {{{2 */
446
447 static void
448 desktop_file_dirs_lock (void)
449 {
450   gint i;
451
452   g_mutex_lock (&desktop_file_dir_lock);
453
454   if (desktop_file_dirs == NULL)
455     {
456       const char * const *data_dirs;
457       GArray *tmp;
458       gint i;
459
460       tmp = g_array_new (FALSE, FALSE, sizeof (DesktopFileDir));
461
462       /* Highest priority: the user's ~/.local/share/applications */
463       desktop_file_dir_create (tmp, g_get_user_data_dir ());
464
465       /* Following that, XDG_DATA_DIRS/applications, in order */
466       data_dirs = g_get_system_data_dirs ();
467       for (i = 0; data_dirs[i]; i++)
468         desktop_file_dir_create (tmp, data_dirs[i]);
469
470       desktop_file_dirs = (DesktopFileDir *) tmp->data;
471       n_desktop_file_dirs = tmp->len;
472
473       g_array_free (tmp, FALSE);
474     }
475
476   for (i = 0; i < n_desktop_file_dirs; i++)
477     if (!desktop_file_dirs[i].is_setup)
478       desktop_file_dir_init (&desktop_file_dirs[i]);
479 }
480
481 static void
482 desktop_file_dirs_unlock (void)
483 {
484   g_mutex_unlock (&desktop_file_dir_lock);
485 }
486
487 static void
488 desktop_file_dirs_refresh (void)
489 {
490   desktop_file_dirs_lock ();
491   desktop_file_dirs_unlock ();
492 }
493
494 static void
495 desktop_file_dirs_invalidate_user (void)
496 {
497   g_mutex_lock (&desktop_file_dir_lock);
498
499   if (n_desktop_file_dirs)
500     desktop_file_dir_reset (&desktop_file_dirs[0]);
501
502   g_mutex_unlock (&desktop_file_dir_lock);
503 }
504
505 /* GDesktopAppInfo implementation {{{1 */
506 /* GObject implementation {{{2 */
507 static void
508 g_desktop_app_info_finalize (GObject *object)
509 {
510   GDesktopAppInfo *info;
511
512   info = G_DESKTOP_APP_INFO (object);
513
514   g_free (info->desktop_id);
515   g_free (info->filename);
516
517   if (info->keyfile)
518     g_key_file_unref (info->keyfile);
519
520   g_free (info->name);
521   g_free (info->generic_name);
522   g_free (info->fullname);
523   g_free (info->comment);
524   g_free (info->icon_name);
525   if (info->icon)
526     g_object_unref (info->icon);
527   g_strfreev (info->keywords);
528   g_strfreev (info->only_show_in);
529   g_strfreev (info->not_show_in);
530   g_free (info->try_exec);
531   g_free (info->exec);
532   g_free (info->binary);
533   g_free (info->path);
534   g_free (info->categories);
535   g_free (info->startup_wm_class);
536   g_strfreev (info->mime_types);
537   g_free (info->app_id);
538   g_strfreev (info->actions);
539
540   G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
541 }
542
543 static void
544 g_desktop_app_info_set_property (GObject      *object,
545                                  guint         prop_id,
546                                  const GValue *value,
547                                  GParamSpec   *pspec)
548 {
549   GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
550
551   switch (prop_id)
552     {
553     case PROP_FILENAME:
554       self->filename = g_value_dup_string (value);
555       break;
556
557     default:
558       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
559       break;
560     }
561 }
562
563 static void
564 g_desktop_app_info_get_property (GObject    *object,
565                                  guint       prop_id,
566                                  GValue     *value,
567                                  GParamSpec *pspec)
568 {
569   GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
570
571   switch (prop_id)
572     {
573     case PROP_FILENAME:
574       g_value_set_string (value, self->filename);
575       break;
576     default:
577       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
578       break;
579     }
580 }
581
582 static void
583 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
584 {
585   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
586
587   gobject_class->get_property = g_desktop_app_info_get_property;
588   gobject_class->set_property = g_desktop_app_info_set_property;
589   gobject_class->finalize = g_desktop_app_info_finalize;
590
591   /**
592    * GDesktopAppInfo:filename:
593    *
594    * The origin filename of this #GDesktopAppInfo
595    */
596   g_object_class_install_property (gobject_class,
597                                    PROP_FILENAME,
598                                    g_param_spec_string ("filename", "Filename", "", NULL,
599                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
600 }
601
602 static void
603 g_desktop_app_info_init (GDesktopAppInfo *local)
604 {
605 }
606
607 /* Construction... {{{2 */
608
609 /*< internal >
610  * binary_from_exec:
611  * @exec: an exec line
612  *
613  * Returns the first word in an exec line (ie: the binary name).
614  *
615  * If @exec is "  progname --foo %F" then returns "progname".
616  */
617 static char *
618 binary_from_exec (const char *exec)
619 {
620   const char *p, *start;
621
622   p = exec;
623   while (*p == ' ')
624     p++;
625   start = p;
626   while (*p != ' ' && *p != 0)
627     p++;
628
629   return g_strndup (start, p - start);
630 }
631
632 static gboolean
633 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
634                                       GKeyFile        *key_file)
635 {
636   char *start_group;
637   char *type;
638   char *try_exec;
639   char *exec;
640   gboolean bus_activatable;
641
642   start_group = g_key_file_get_start_group (key_file);
643   if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
644     {
645       g_free (start_group);
646       return FALSE;
647     }
648   g_free (start_group);
649
650   type = g_key_file_get_string (key_file,
651                                 G_KEY_FILE_DESKTOP_GROUP,
652                                 G_KEY_FILE_DESKTOP_KEY_TYPE,
653                                 NULL);
654   if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
655     {
656       g_free (type);
657       return FALSE;
658     }
659   g_free (type);
660
661   try_exec = g_key_file_get_string (key_file,
662                                     G_KEY_FILE_DESKTOP_GROUP,
663                                     G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
664                                     NULL);
665   if (try_exec && try_exec[0] != '\0')
666     {
667       char *t;
668       t = g_find_program_in_path (try_exec);
669       if (t == NULL)
670         {
671           g_free (try_exec);
672           return FALSE;
673         }
674       g_free (t);
675     }
676
677   exec = g_key_file_get_string (key_file,
678                                 G_KEY_FILE_DESKTOP_GROUP,
679                                 G_KEY_FILE_DESKTOP_KEY_EXEC,
680                                 NULL);
681   if (exec && exec[0] != '\0')
682     {
683       gint argc;
684       char **argv;
685       if (!g_shell_parse_argv (exec, &argc, &argv, NULL))
686         {
687           g_free (exec);
688           g_free (try_exec);
689           return FALSE;
690         }
691       else
692         {
693           char *t;
694           t = g_find_program_in_path (argv[0]);
695           g_strfreev (argv);
696
697           if (t == NULL)
698             {
699               g_free (exec);
700               g_free (try_exec);
701               return FALSE;
702             }
703           g_free (t);
704         }
705     }
706
707   info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
708   info->generic_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, GENERIC_NAME_KEY, NULL, NULL);
709   info->fullname = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, FULL_NAME_KEY, NULL, NULL);
710   info->keywords = g_key_file_get_locale_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, KEYWORDS_KEY, NULL, NULL, NULL);
711   info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
712   info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
713   info->icon_name =  g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
714   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);
715   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);
716   info->try_exec = try_exec;
717   info->exec = exec;
718   info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
719   info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
720   info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
721   info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
722   info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
723   info->categories = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL);
724   info->startup_wm_class = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, STARTUP_WM_CLASS_KEY, NULL);
725   info->mime_types = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL, NULL);
726   bus_activatable = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE, NULL);
727   info->actions = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ACTIONS, NULL, NULL);
728
729   /* Remove the special-case: no Actions= key just means 0 extra actions */
730   if (info->actions == NULL)
731     info->actions = g_new0 (gchar *, 0 + 1);
732
733   info->icon = NULL;
734   if (info->icon_name)
735     {
736       if (g_path_is_absolute (info->icon_name))
737         {
738           GFile *file;
739
740           file = g_file_new_for_path (info->icon_name);
741           info->icon = g_file_icon_new (file);
742           g_object_unref (file);
743         }
744       else
745         {
746           char *p;
747
748           /* Work around a common mistake in desktop files */
749           if ((p = strrchr (info->icon_name, '.')) != NULL &&
750               (strcmp (p, ".png") == 0 ||
751                strcmp (p, ".xpm") == 0 ||
752                strcmp (p, ".svg") == 0))
753             *p = 0;
754
755           info->icon = g_themed_icon_new (info->icon_name);
756         }
757     }
758
759   if (info->exec)
760     info->binary = binary_from_exec (info->exec);
761
762   if (info->path && info->path[0] == '\0')
763     {
764       g_free (info->path);
765       info->path = NULL;
766     }
767
768   /* Can only be DBusActivatable if we know the filename, which means
769    * that this won't work for the load-from-keyfile case.
770    */
771   if (bus_activatable && info->filename)
772     {
773       gchar *basename;
774       gchar *last_dot;
775
776       basename = g_path_get_basename (info->filename);
777       last_dot = strrchr (basename, '.');
778
779       if (last_dot && g_str_equal (last_dot, ".desktop"))
780         {
781           *last_dot = '\0';
782
783           if (g_dbus_is_interface_name (basename))
784             info->app_id = g_strdup (basename);
785         }
786
787       g_free (basename);
788     }
789
790   info->keyfile = g_key_file_ref (key_file);
791
792   return TRUE;
793 }
794
795 static gboolean
796 g_desktop_app_info_load_file (GDesktopAppInfo *self)
797 {
798   GKeyFile *key_file;
799   gboolean retval = FALSE;
800
801   g_return_val_if_fail (self->filename != NULL, FALSE);
802
803   self->desktop_id = g_path_get_basename (self->filename);
804
805   key_file = g_key_file_new ();
806
807   if (g_key_file_load_from_file (key_file, self->filename, G_KEY_FILE_NONE, NULL))
808     retval = g_desktop_app_info_load_from_keyfile (self, key_file);
809
810   g_key_file_unref (key_file);
811   return retval;
812 }
813
814 /**
815  * g_desktop_app_info_new_from_keyfile:
816  * @key_file: an opened #GKeyFile
817  *
818  * Creates a new #GDesktopAppInfo.
819  *
820  * Returns: a new #GDesktopAppInfo or %NULL on error.
821  *
822  * Since: 2.18
823  **/
824 GDesktopAppInfo *
825 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
826 {
827   GDesktopAppInfo *info;
828
829   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
830   info->filename = NULL;
831   if (!g_desktop_app_info_load_from_keyfile (info, key_file))
832     {
833       g_object_unref (info);
834       return NULL;
835     }
836   return info;
837 }
838
839 /**
840  * g_desktop_app_info_new_from_filename:
841  * @filename: the path of a desktop file, in the GLib filename encoding
842  *
843  * Creates a new #GDesktopAppInfo.
844  *
845  * Returns: a new #GDesktopAppInfo or %NULL on error.
846  **/
847 GDesktopAppInfo *
848 g_desktop_app_info_new_from_filename (const char *filename)
849 {
850   GDesktopAppInfo *info = NULL;
851
852   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
853   if (!g_desktop_app_info_load_file (info))
854     {
855       g_object_unref (info);
856       return NULL;
857     }
858   return info;
859 }
860
861 /**
862  * g_desktop_app_info_new:
863  * @desktop_id: the desktop file id
864  *
865  * Creates a new #GDesktopAppInfo based on a desktop file id.
866  *
867  * A desktop file id is the basename of the desktop file, including the
868  * .desktop extension. GIO is looking for a desktop file with this name
869  * in the <filename>applications</filename> subdirectories of the XDG data
870  * directories (i.e. the directories specified in the
871  * <envar>XDG_DATA_HOME</envar> and <envar>XDG_DATA_DIRS</envar> environment
872  * variables). GIO also supports the prefix-to-subdirectory mapping that is
873  * described in the <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Menu Spec</ulink>
874  * (i.e. a desktop id of kde-foo.desktop will match
875  * <filename>/usr/share/applications/kde/foo.desktop</filename>).
876  *
877  * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
878  */
879 GDesktopAppInfo *
880 g_desktop_app_info_new (const char *desktop_id)
881 {
882   GDesktopAppInfo *appinfo = NULL;
883   guint i;
884
885   desktop_file_dirs_lock ();
886
887   for (i = 0; i < n_desktop_file_dirs; i++)
888     {
889       appinfo = desktop_file_dir_get_app (&desktop_file_dirs[i], desktop_id);
890
891       if (appinfo)
892         break;
893     }
894
895   desktop_file_dirs_unlock ();
896
897   if (appinfo == NULL)
898     return NULL;
899
900   g_free (appinfo->desktop_id);
901   appinfo->desktop_id = g_strdup (desktop_id);
902
903   if (g_desktop_app_info_get_is_hidden (appinfo))
904     {
905       g_object_unref (appinfo);
906       appinfo = NULL;
907     }
908
909   return appinfo;
910 }
911
912 static GAppInfo *
913 g_desktop_app_info_dup (GAppInfo *appinfo)
914 {
915   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
916   GDesktopAppInfo *new_info;
917
918   new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
919
920   new_info->filename = g_strdup (info->filename);
921   new_info->desktop_id = g_strdup (info->desktop_id);
922
923   if (info->keyfile)
924     new_info->keyfile = g_key_file_ref (info->keyfile);
925
926   new_info->name = g_strdup (info->name);
927   new_info->generic_name = g_strdup (info->generic_name);
928   new_info->fullname = g_strdup (info->fullname);
929   new_info->keywords = g_strdupv (info->keywords);
930   new_info->comment = g_strdup (info->comment);
931   new_info->nodisplay = info->nodisplay;
932   new_info->icon_name = g_strdup (info->icon_name);
933   if (info->icon)
934     new_info->icon = g_object_ref (info->icon);
935   new_info->only_show_in = g_strdupv (info->only_show_in);
936   new_info->not_show_in = g_strdupv (info->not_show_in);
937   new_info->try_exec = g_strdup (info->try_exec);
938   new_info->exec = g_strdup (info->exec);
939   new_info->binary = g_strdup (info->binary);
940   new_info->path = g_strdup (info->path);
941   new_info->app_id = g_strdup (info->app_id);
942   new_info->hidden = info->hidden;
943   new_info->terminal = info->terminal;
944   new_info->startup_notify = info->startup_notify;
945
946   return G_APP_INFO (new_info);
947 }
948
949 /* GAppInfo interface implementation functions {{{2 */
950
951 static gboolean
952 g_desktop_app_info_equal (GAppInfo *appinfo1,
953                           GAppInfo *appinfo2)
954 {
955   GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
956   GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
957
958   if (info1->desktop_id == NULL ||
959       info2->desktop_id == NULL)
960     return info1 == info2;
961
962   return strcmp (info1->desktop_id, info2->desktop_id) == 0;
963 }
964
965 static const char *
966 g_desktop_app_info_get_id (GAppInfo *appinfo)
967 {
968   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
969
970   return info->desktop_id;
971 }
972
973 static const char *
974 g_desktop_app_info_get_name (GAppInfo *appinfo)
975 {
976   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
977
978   if (info->name == NULL)
979     return _("Unnamed");
980   return info->name;
981 }
982
983 static const char *
984 g_desktop_app_info_get_display_name (GAppInfo *appinfo)
985 {
986   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
987
988   if (info->fullname == NULL)
989     return g_desktop_app_info_get_name (appinfo);
990   return info->fullname;
991 }
992
993 /**
994  * g_desktop_app_info_get_is_hidden:
995  * @info: a #GDesktopAppInfo.
996  *
997  * A desktop file is hidden if the Hidden key in it is
998  * set to True.
999  *
1000  * Returns: %TRUE if hidden, %FALSE otherwise.
1001  **/
1002 gboolean
1003 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
1004 {
1005   return info->hidden;
1006 }
1007
1008 /**
1009  * g_desktop_app_info_get_filename:
1010  * @info: a #GDesktopAppInfo
1011  *
1012  * When @info was created from a known filename, return it.  In some
1013  * situations such as the #GDesktopAppInfo returned from
1014  * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
1015  *
1016  * Returns: The full path to the file for @info, or %NULL if not known.
1017  * Since: 2.24
1018  */
1019 const char *
1020 g_desktop_app_info_get_filename (GDesktopAppInfo *info)
1021 {
1022   return info->filename;
1023 }
1024
1025 static const char *
1026 g_desktop_app_info_get_description (GAppInfo *appinfo)
1027 {
1028   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1029
1030   return info->comment;
1031 }
1032
1033 static const char *
1034 g_desktop_app_info_get_executable (GAppInfo *appinfo)
1035 {
1036   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1037
1038   return info->binary;
1039 }
1040
1041 static const char *
1042 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
1043 {
1044   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1045
1046   return info->exec;
1047 }
1048
1049 static GIcon *
1050 g_desktop_app_info_get_icon (GAppInfo *appinfo)
1051 {
1052   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1053
1054   return info->icon;
1055 }
1056
1057 /**
1058  * g_desktop_app_info_get_categories:
1059  * @info: a #GDesktopAppInfo
1060  *
1061  * Gets the categories from the desktop file.
1062  *
1063  * Returns: The unparsed Categories key from the desktop file;
1064  *     i.e. no attempt is made to split it by ';' or validate it.
1065  */
1066 const char *
1067 g_desktop_app_info_get_categories (GDesktopAppInfo *info)
1068 {
1069   return info->categories;
1070 }
1071
1072 /**
1073  * g_desktop_app_info_get_keywords:
1074  * @info: a #GDesktopAppInfo
1075  *
1076  * Gets the keywords from the desktop file.
1077  *
1078  * Returns: (transfer none): The value of the Keywords key
1079  *
1080  * Since: 2.32
1081  */
1082 const char * const *
1083 g_desktop_app_info_get_keywords (GDesktopAppInfo *info)
1084 {
1085   return (const char * const *)info->keywords;
1086 }
1087
1088 /**
1089  * g_desktop_app_info_get_generic_name:
1090  * @info: a #GDesktopAppInfo
1091  *
1092  * Gets the generic name from the destkop file.
1093  *
1094  * Returns: The value of the GenericName key
1095  */
1096 const char *
1097 g_desktop_app_info_get_generic_name (GDesktopAppInfo *info)
1098 {
1099   return info->generic_name;
1100 }
1101
1102 /**
1103  * g_desktop_app_info_get_nodisplay:
1104  * @info: a #GDesktopAppInfo
1105  *
1106  * Gets the value of the NoDisplay key, which helps determine if the
1107  * application info should be shown in menus. See
1108  * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
1109  *
1110  * Returns: The value of the NoDisplay key
1111  *
1112  * Since: 2.30
1113  */
1114 gboolean
1115 g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info)
1116 {
1117   return info->nodisplay;
1118 }
1119
1120 /**
1121  * g_desktop_app_info_get_show_in:
1122  * @info: a #GDesktopAppInfo
1123  * @desktop_env: a string specifying a desktop name
1124  *
1125  * Checks if the application info should be shown in menus that list available
1126  * applications for a specific name of the desktop, based on the
1127  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal> keys.
1128  *
1129  * If @desktop_env is %NULL, then the name of the desktop set with
1130  * g_desktop_app_info_set_desktop_env() is used.
1131  *
1132  * Note that g_app_info_should_show() for @info will include this check (with
1133  * %NULL for @desktop_env) as well as additional checks.
1134  *
1135  * Returns: %TRUE if the @info should be shown in @desktop_env according to the
1136  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal> keys, %FALSE
1137  * otherwise.
1138  *
1139  * Since: 2.30
1140  */
1141 gboolean
1142 g_desktop_app_info_get_show_in (GDesktopAppInfo *info,
1143                                 const gchar     *desktop_env)
1144 {
1145   gboolean found;
1146   int i;
1147
1148   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
1149
1150   if (!desktop_env) {
1151     G_LOCK (g_desktop_env);
1152     desktop_env = g_desktop_env;
1153     G_UNLOCK (g_desktop_env);
1154   }
1155
1156   if (info->only_show_in)
1157     {
1158       if (desktop_env == NULL)
1159         return FALSE;
1160
1161       found = FALSE;
1162       for (i = 0; info->only_show_in[i] != NULL; i++)
1163         {
1164           if (strcmp (info->only_show_in[i], desktop_env) == 0)
1165             {
1166               found = TRUE;
1167               break;
1168             }
1169         }
1170       if (!found)
1171         return FALSE;
1172     }
1173
1174   if (info->not_show_in && desktop_env)
1175     {
1176       for (i = 0; info->not_show_in[i] != NULL; i++)
1177         {
1178           if (strcmp (info->not_show_in[i], desktop_env) == 0)
1179             return FALSE;
1180         }
1181     }
1182
1183   return TRUE;
1184 }
1185
1186 /* Launching... {{{2 */
1187
1188 static char *
1189 expand_macro_single (char macro, char *uri)
1190 {
1191   GFile *file;
1192   char *result = NULL;
1193   char *path = NULL;
1194   char *name;
1195
1196   file = g_file_new_for_uri (uri);
1197
1198   switch (macro)
1199     {
1200     case 'u':
1201     case 'U':
1202       result = g_shell_quote (uri);
1203       break;
1204     case 'f':
1205     case 'F':
1206       path = g_file_get_path (file);
1207       if (path)
1208         result = g_shell_quote (path);
1209       break;
1210     case 'd':
1211     case 'D':
1212       path = g_file_get_path (file);
1213       if (path)
1214         {
1215           name = g_path_get_dirname (path);
1216           result = g_shell_quote (name);
1217           g_free (name);
1218         }
1219       break;
1220     case 'n':
1221     case 'N':
1222       path = g_file_get_path (file);
1223       if (path)
1224         {
1225           name = g_path_get_basename (path);
1226           result = g_shell_quote (name);
1227           g_free (name);
1228         }
1229       break;
1230     }
1231
1232   g_object_unref (file);
1233   g_free (path);
1234
1235   return result;
1236 }
1237
1238 static void
1239 expand_macro (char              macro,
1240               GString          *exec,
1241               GDesktopAppInfo  *info,
1242               GList           **uri_list)
1243 {
1244   GList *uris = *uri_list;
1245   char *expanded;
1246   gboolean force_file_uri;
1247   char force_file_uri_macro;
1248   char *uri;
1249
1250   g_return_if_fail (exec != NULL);
1251
1252   /* On %u and %U, pass POSIX file path pointing to the URI via
1253    * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
1254    * running or the URI doesn't have a POSIX file path via FUSE
1255    * we'll just pass the URI.
1256    */
1257   force_file_uri_macro = macro;
1258   force_file_uri = FALSE;
1259   if (!info->no_fuse)
1260     {
1261       switch (macro)
1262         {
1263         case 'u':
1264           force_file_uri_macro = 'f';
1265           force_file_uri = TRUE;
1266           break;
1267         case 'U':
1268           force_file_uri_macro = 'F';
1269           force_file_uri = TRUE;
1270           break;
1271         default:
1272           break;
1273         }
1274     }
1275
1276   switch (macro)
1277     {
1278     case 'u':
1279     case 'f':
1280     case 'd':
1281     case 'n':
1282       if (uris)
1283         {
1284           uri = uris->data;
1285           if (!force_file_uri ||
1286               /* Pass URI if it contains an anchor */
1287               strchr (uri, '#') != NULL)
1288             {
1289               expanded = expand_macro_single (macro, uri);
1290             }
1291           else
1292             {
1293               expanded = expand_macro_single (force_file_uri_macro, uri);
1294               if (expanded == NULL)
1295                 expanded = expand_macro_single (macro, uri);
1296             }
1297
1298           if (expanded)
1299             {
1300               g_string_append (exec, expanded);
1301               g_free (expanded);
1302             }
1303           uris = uris->next;
1304         }
1305
1306       break;
1307
1308     case 'U':
1309     case 'F':
1310     case 'D':
1311     case 'N':
1312       while (uris)
1313         {
1314           uri = uris->data;
1315
1316           if (!force_file_uri ||
1317               /* Pass URI if it contains an anchor */
1318               strchr (uri, '#') != NULL)
1319             {
1320               expanded = expand_macro_single (macro, uri);
1321             }
1322           else
1323             {
1324               expanded = expand_macro_single (force_file_uri_macro, uri);
1325               if (expanded == NULL)
1326                 expanded = expand_macro_single (macro, uri);
1327             }
1328
1329           if (expanded)
1330             {
1331               g_string_append (exec, expanded);
1332               g_free (expanded);
1333             }
1334
1335           uris = uris->next;
1336
1337           if (uris != NULL && expanded)
1338             g_string_append_c (exec, ' ');
1339         }
1340
1341       break;
1342
1343     case 'i':
1344       if (info->icon_name)
1345         {
1346           g_string_append (exec, "--icon ");
1347           expanded = g_shell_quote (info->icon_name);
1348           g_string_append (exec, expanded);
1349           g_free (expanded);
1350         }
1351       break;
1352
1353     case 'c':
1354       if (info->name)
1355         {
1356           expanded = g_shell_quote (info->name);
1357           g_string_append (exec, expanded);
1358           g_free (expanded);
1359         }
1360       break;
1361
1362     case 'k':
1363       if (info->filename)
1364         {
1365           expanded = g_shell_quote (info->filename);
1366           g_string_append (exec, expanded);
1367           g_free (expanded);
1368         }
1369       break;
1370
1371     case 'm': /* deprecated */
1372       break;
1373
1374     case '%':
1375       g_string_append_c (exec, '%');
1376       break;
1377     }
1378
1379   *uri_list = uris;
1380 }
1381
1382 static gboolean
1383 expand_application_parameters (GDesktopAppInfo   *info,
1384                                const gchar       *exec_line,
1385                                GList            **uris,
1386                                int               *argc,
1387                                char            ***argv,
1388                                GError           **error)
1389 {
1390   GList *uri_list = *uris;
1391   const char *p = exec_line;
1392   GString *expanded_exec;
1393   gboolean res;
1394
1395   if (exec_line == NULL)
1396     {
1397       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1398                            _("Desktop file didn't specify Exec field"));
1399       return FALSE;
1400     }
1401
1402   expanded_exec = g_string_new (NULL);
1403
1404   while (*p)
1405     {
1406       if (p[0] == '%' && p[1] != '\0')
1407         {
1408           expand_macro (p[1], expanded_exec, info, uris);
1409           p++;
1410         }
1411       else
1412         g_string_append_c (expanded_exec, *p);
1413
1414       p++;
1415     }
1416
1417   /* No file substitutions */
1418   if (uri_list == *uris && uri_list != NULL)
1419     {
1420       /* If there is no macro default to %f. This is also what KDE does */
1421       g_string_append_c (expanded_exec, ' ');
1422       expand_macro ('f', expanded_exec, info, uris);
1423     }
1424
1425   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
1426   g_string_free (expanded_exec, TRUE);
1427   return res;
1428 }
1429
1430 static gboolean
1431 prepend_terminal_to_vector (int    *argc,
1432                             char ***argv)
1433 {
1434 #ifndef G_OS_WIN32
1435   char **real_argv;
1436   int real_argc;
1437   int i, j;
1438   char **term_argv = NULL;
1439   int term_argc = 0;
1440   char *check;
1441   char **the_argv;
1442
1443   g_return_val_if_fail (argc != NULL, FALSE);
1444   g_return_val_if_fail (argv != NULL, FALSE);
1445
1446   /* sanity */
1447   if(*argv == NULL)
1448     *argc = 0;
1449
1450   the_argv = *argv;
1451
1452   /* compute size if not given */
1453   if (*argc < 0)
1454     {
1455       for (i = 0; the_argv[i] != NULL; i++)
1456         ;
1457       *argc = i;
1458     }
1459
1460   term_argc = 2;
1461   term_argv = g_new0 (char *, 3);
1462
1463   check = g_find_program_in_path ("gnome-terminal");
1464   if (check != NULL)
1465     {
1466       term_argv[0] = check;
1467       /* Note that gnome-terminal takes -x and
1468        * as -e in gnome-terminal is broken we use that. */
1469       term_argv[1] = g_strdup ("-x");
1470     }
1471   else
1472     {
1473       if (check == NULL)
1474         check = g_find_program_in_path ("nxterm");
1475       if (check == NULL)
1476         check = g_find_program_in_path ("color-xterm");
1477       if (check == NULL)
1478         check = g_find_program_in_path ("rxvt");
1479       if (check == NULL)
1480         check = g_find_program_in_path ("xterm");
1481       if (check == NULL)
1482         check = g_find_program_in_path ("dtterm");
1483       if (check == NULL)
1484         {
1485           check = g_strdup ("xterm");
1486           g_warning ("couldn't find a terminal, falling back to xterm");
1487         }
1488       term_argv[0] = check;
1489       term_argv[1] = g_strdup ("-e");
1490     }
1491
1492   real_argc = term_argc + *argc;
1493   real_argv = g_new (char *, real_argc + 1);
1494
1495   for (i = 0; i < term_argc; i++)
1496     real_argv[i] = term_argv[i];
1497
1498   for (j = 0; j < *argc; j++, i++)
1499     real_argv[i] = (char *)the_argv[j];
1500
1501   real_argv[i] = NULL;
1502
1503   g_free (*argv);
1504   *argv = real_argv;
1505   *argc = real_argc;
1506
1507   /* we use g_free here as we sucked all the inner strings
1508    * out from it into real_argv */
1509   g_free (term_argv);
1510   return TRUE;
1511 #else
1512   return FALSE;
1513 #endif /* G_OS_WIN32 */
1514 }
1515
1516 static GList *
1517 create_files_for_uris (GList *uris)
1518 {
1519   GList *res;
1520   GList *iter;
1521
1522   res = NULL;
1523
1524   for (iter = uris; iter; iter = iter->next)
1525     {
1526       GFile *file = g_file_new_for_uri ((char *)iter->data);
1527       res = g_list_prepend (res, file);
1528     }
1529
1530   return g_list_reverse (res);
1531 }
1532
1533 typedef struct
1534 {
1535   GSpawnChildSetupFunc user_setup;
1536   gpointer user_setup_data;
1537
1538   char *pid_envvar;
1539 } ChildSetupData;
1540
1541 static void
1542 child_setup (gpointer user_data)
1543 {
1544   ChildSetupData *data = user_data;
1545
1546   if (data->pid_envvar)
1547     {
1548       pid_t pid = getpid ();
1549       char buf[20];
1550       int i;
1551
1552       /* Write the pid into the space already reserved for it in the
1553        * environment array. We can't use sprintf because it might
1554        * malloc, so we do it by hand. It's simplest to write the pid
1555        * out backwards first, then copy it over.
1556        */
1557       for (i = 0; pid; i++, pid /= 10)
1558         buf[i] = (pid % 10) + '0';
1559       for (i--; i >= 0; i--)
1560         *(data->pid_envvar++) = buf[i];
1561       *data->pid_envvar = '\0';
1562     }
1563
1564   if (data->user_setup)
1565     data->user_setup (data->user_setup_data);
1566 }
1567
1568 static void
1569 notify_desktop_launch (GDBusConnection  *session_bus,
1570                        GDesktopAppInfo  *info,
1571                        long              pid,
1572                        const char       *display,
1573                        const char       *sn_id,
1574                        GList            *uris)
1575 {
1576   GDBusMessage *msg;
1577   GVariantBuilder uri_variant;
1578   GVariantBuilder extras_variant;
1579   GList *iter;
1580   const char *desktop_file_id;
1581   const char *gio_desktop_file;
1582
1583   if (session_bus == NULL)
1584     return;
1585
1586   g_variant_builder_init (&uri_variant, G_VARIANT_TYPE ("as"));
1587   for (iter = uris; iter; iter = iter->next)
1588     g_variant_builder_add (&uri_variant, "s", iter->data);
1589
1590   g_variant_builder_init (&extras_variant, G_VARIANT_TYPE ("a{sv}"));
1591   if (sn_id != NULL && g_utf8_validate (sn_id, -1, NULL))
1592     g_variant_builder_add (&extras_variant, "{sv}",
1593                            "startup-id",
1594                            g_variant_new ("s",
1595                                           sn_id));
1596   gio_desktop_file = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
1597   if (gio_desktop_file != NULL)
1598     g_variant_builder_add (&extras_variant, "{sv}",
1599                            "origin-desktop-file",
1600                            g_variant_new_bytestring (gio_desktop_file));
1601   if (g_get_prgname () != NULL)
1602     g_variant_builder_add (&extras_variant, "{sv}",
1603                            "origin-prgname",
1604                            g_variant_new_bytestring (g_get_prgname ()));
1605   g_variant_builder_add (&extras_variant, "{sv}",
1606                          "origin-pid",
1607                          g_variant_new ("x",
1608                                         (gint64)getpid ()));
1609
1610   if (info->filename)
1611     desktop_file_id = info->filename;
1612   else if (info->desktop_id)
1613     desktop_file_id = info->desktop_id;
1614   else
1615     desktop_file_id = "";
1616
1617   msg = g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
1618                                    "org.gtk.gio.DesktopAppInfo",
1619                                    "Launched");
1620   g_dbus_message_set_body (msg, g_variant_new ("(@aysxasa{sv})",
1621                                                g_variant_new_bytestring (desktop_file_id),
1622                                                display ? display : "",
1623                                                (gint64)pid,
1624                                                &uri_variant,
1625                                                &extras_variant));
1626   g_dbus_connection_send_message (session_bus,
1627                                   msg, 0,
1628                                   NULL,
1629                                   NULL);
1630   g_object_unref (msg);
1631 }
1632
1633 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
1634
1635 static gboolean
1636 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
1637                                            GDBusConnection            *session_bus,
1638                                            const gchar                *exec_line,
1639                                            GList                      *uris,
1640                                            GAppLaunchContext          *launch_context,
1641                                            GSpawnFlags                 spawn_flags,
1642                                            GSpawnChildSetupFunc        user_setup,
1643                                            gpointer                    user_setup_data,
1644                                            GDesktopAppLaunchCallback   pid_callback,
1645                                            gpointer                    pid_callback_data,
1646                                            GError                    **error)
1647 {
1648   gboolean completed = FALSE;
1649   GList *old_uris;
1650   char **argv, **envp;
1651   int argc;
1652   ChildSetupData data;
1653
1654   g_return_val_if_fail (info != NULL, FALSE);
1655
1656   argv = NULL;
1657
1658   if (launch_context)
1659     envp = g_app_launch_context_get_environment (launch_context);
1660   else
1661     envp = g_get_environ ();
1662
1663   do
1664     {
1665       GPid pid;
1666       GList *launched_uris;
1667       GList *iter;
1668       char *display, *sn_id;
1669
1670       old_uris = uris;
1671       if (!expand_application_parameters (info, exec_line, &uris, &argc, &argv, error))
1672         goto out;
1673
1674       /* Get the subset of URIs we're launching with this process */
1675       launched_uris = NULL;
1676       for (iter = old_uris; iter != NULL && iter != uris; iter = iter->next)
1677         launched_uris = g_list_prepend (launched_uris, iter->data);
1678       launched_uris = g_list_reverse (launched_uris);
1679
1680       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
1681         {
1682           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1683                                _("Unable to find terminal required for application"));
1684           goto out;
1685         }
1686
1687       data.user_setup = user_setup;
1688       data.user_setup_data = user_setup_data;
1689
1690       if (info->filename)
1691         {
1692           envp = g_environ_setenv (envp,
1693                                    "GIO_LAUNCHED_DESKTOP_FILE",
1694                                    info->filename,
1695                                    TRUE);
1696           envp = g_environ_setenv (envp,
1697                                    "GIO_LAUNCHED_DESKTOP_FILE_PID",
1698                                    "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
1699                                    TRUE);
1700           data.pid_envvar = (char *)g_environ_getenv (envp, "GIO_LAUNCHED_DESKTOP_FILE_PID");
1701         }
1702       else
1703         {
1704           data.pid_envvar = NULL;
1705         }
1706
1707       display = NULL;
1708       sn_id = NULL;
1709       if (launch_context)
1710         {
1711           GList *launched_files = create_files_for_uris (launched_uris);
1712
1713           display = g_app_launch_context_get_display (launch_context,
1714                                                       G_APP_INFO (info),
1715                                                       launched_files);
1716           if (display)
1717             envp = g_environ_setenv (envp, "DISPLAY", display, TRUE);
1718
1719           if (info->startup_notify)
1720             {
1721               sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
1722                                                                   G_APP_INFO (info),
1723                                                                   launched_files);
1724               envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE);
1725             }
1726
1727           g_list_free_full (launched_files, g_object_unref);
1728         }
1729
1730       if (!g_spawn_async (info->path,
1731                           argv,
1732                           envp,
1733                           spawn_flags,
1734                           child_setup,
1735                           &data,
1736                           &pid,
1737                           error))
1738         {
1739           if (sn_id)
1740             g_app_launch_context_launch_failed (launch_context, sn_id);
1741
1742           g_free (display);
1743           g_free (sn_id);
1744           g_list_free (launched_uris);
1745
1746           goto out;
1747         }
1748
1749       if (pid_callback != NULL)
1750         pid_callback (info, pid, pid_callback_data);
1751
1752       if (launch_context != NULL)
1753         {
1754           GVariantBuilder builder;
1755           GVariant *platform_data;
1756
1757           g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1758           g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 (pid));
1759           if (sn_id)
1760             g_variant_builder_add (&builder, "{sv}", "startup-notification-id", g_variant_new_string (sn_id));
1761           platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
1762           g_signal_emit_by_name (launch_context, "launched", info, platform_data);
1763           g_variant_unref (platform_data);
1764         }
1765
1766       notify_desktop_launch (session_bus,
1767                              info,
1768                              pid,
1769                              display,
1770                              sn_id,
1771                              launched_uris);
1772
1773       g_free (display);
1774       g_free (sn_id);
1775       g_list_free (launched_uris);
1776
1777       g_strfreev (argv);
1778       argv = NULL;
1779     }
1780   while (uris != NULL);
1781
1782   completed = TRUE;
1783
1784  out:
1785   g_strfreev (argv);
1786   g_strfreev (envp);
1787
1788   return completed;
1789 }
1790
1791 static gchar *
1792 object_path_from_appid (const gchar *app_id)
1793 {
1794   gchar *path;
1795   gint i, n;
1796
1797   n = strlen (app_id);
1798   path = g_malloc (n + 2);
1799
1800   path[0] = '/';
1801
1802   for (i = 0; i < n; i++)
1803     if (app_id[i] != '.')
1804       path[i + 1] = app_id[i];
1805     else
1806       path[i + 1] = '/';
1807
1808   path[i + 1] = '\0';
1809
1810   return path;
1811 }
1812
1813 static GVariant *
1814 g_desktop_app_info_make_platform_data (GDesktopAppInfo   *info,
1815                                        GList             *uris,
1816                                        GAppLaunchContext *launch_context)
1817 {
1818   GVariantBuilder builder;
1819
1820   g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
1821
1822   if (launch_context)
1823     {
1824       GList *launched_files = create_files_for_uris (uris);
1825
1826       if (info->startup_notify)
1827         {
1828           gchar *sn_id;
1829
1830           sn_id = g_app_launch_context_get_startup_notify_id (launch_context, G_APP_INFO (info), launched_files);
1831           g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id));
1832         }
1833
1834       g_list_free_full (launched_files, g_object_unref);
1835     }
1836
1837   return g_variant_builder_end (&builder);
1838 }
1839
1840 static gboolean
1841 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo    *info,
1842                                           GDBusConnection    *session_bus,
1843                                           GList              *uris,
1844                                           GAppLaunchContext  *launch_context)
1845 {
1846   GVariantBuilder builder;
1847   gchar *object_path;
1848
1849   g_return_val_if_fail (info != NULL, FALSE);
1850
1851   g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
1852
1853   if (uris)
1854     {
1855       GList *iter;
1856
1857       g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
1858       for (iter = uris; iter; iter = iter->next)
1859         g_variant_builder_add (&builder, "s", iter->data);
1860       g_variant_builder_close (&builder);
1861     }
1862
1863   g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
1864
1865   /* This is non-blocking API.  Similar to launching via fork()/exec()
1866    * we don't wait around to see if the program crashed during startup.
1867    * This is what startup-notification's job is...
1868    */
1869   object_path = object_path_from_appid (info->app_id);
1870   g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
1871                           uris ? "Open" : "Activate", g_variant_builder_end (&builder),
1872                           NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
1873   g_free (object_path);
1874
1875   return TRUE;
1876 }
1877
1878 static gboolean
1879 g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
1880                                          GList                      *uris,
1881                                          GAppLaunchContext          *launch_context,
1882                                          GSpawnFlags                 spawn_flags,
1883                                          GSpawnChildSetupFunc        user_setup,
1884                                          gpointer                    user_setup_data,
1885                                          GDesktopAppLaunchCallback   pid_callback,
1886                                          gpointer                    pid_callback_data,
1887                                          GError                     **error)
1888 {
1889   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1890   GDBusConnection *session_bus;
1891   gboolean success = TRUE;
1892
1893   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1894
1895   if (session_bus && info->app_id)
1896     g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context);
1897   else
1898     success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
1899                                                          spawn_flags, user_setup, user_setup_data,
1900                                                          pid_callback, pid_callback_data, error);
1901
1902   if (session_bus != NULL)
1903     {
1904       /* This asynchronous flush holds a reference until it completes,
1905        * which ensures that the following unref won't immediately kill
1906        * the connection if we were the initial owner.
1907        */
1908       g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
1909       g_object_unref (session_bus);
1910     }
1911
1912   return success;
1913 }
1914
1915 static gboolean
1916 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
1917                                 GList              *uris,
1918                                 GAppLaunchContext  *launch_context,
1919                                 GError            **error)
1920 {
1921   return g_desktop_app_info_launch_uris_internal (appinfo, uris,
1922                                                   launch_context,
1923                                                   _SPAWN_FLAGS_DEFAULT,
1924                                                   NULL, NULL, NULL, NULL,
1925                                                   error);
1926 }
1927
1928 static gboolean
1929 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1930 {
1931   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1932
1933   return info->exec &&
1934     ((strstr (info->exec, "%u") != NULL) ||
1935      (strstr (info->exec, "%U") != NULL));
1936 }
1937
1938 static gboolean
1939 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1940 {
1941   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1942
1943   return info->exec &&
1944     ((strstr (info->exec, "%f") != NULL) ||
1945      (strstr (info->exec, "%F") != NULL));
1946 }
1947
1948 static gboolean
1949 g_desktop_app_info_launch (GAppInfo           *appinfo,
1950                            GList              *files,
1951                            GAppLaunchContext  *launch_context,
1952                            GError            **error)
1953 {
1954   GList *uris;
1955   char *uri;
1956   gboolean res;
1957
1958   uris = NULL;
1959   while (files)
1960     {
1961       uri = g_file_get_uri (files->data);
1962       uris = g_list_prepend (uris, uri);
1963       files = files->next;
1964     }
1965
1966   uris = g_list_reverse (uris);
1967
1968   res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1969
1970   g_list_free_full (uris, g_free);
1971
1972   return res;
1973 }
1974
1975 /**
1976  * g_desktop_app_info_launch_uris_as_manager:
1977  * @appinfo: a #GDesktopAppInfo
1978  * @uris: (element-type utf8): List of URIs
1979  * @launch_context: a #GAppLaunchContext
1980  * @spawn_flags: #GSpawnFlags, used for each process
1981  * @user_setup: (scope call): a #GSpawnChildSetupFunc, used once for
1982  *     each process.
1983  * @user_setup_data: (closure user_setup): User data for @user_setup
1984  * @pid_callback: (scope call): Callback for child processes
1985  * @pid_callback_data: (closure pid_callback): User data for @callback
1986  * @error: return location for a #GError, or %NULL
1987  *
1988  * This function performs the equivalent of g_app_info_launch_uris(),
1989  * but is intended primarily for operating system components that
1990  * launch applications.  Ordinary applications should use
1991  * g_app_info_launch_uris().
1992  *
1993  * If the application is launched via traditional UNIX fork()/exec()
1994  * then @spawn_flags, @user_setup and @user_setup_data are used for the
1995  * call to g_spawn_async().  Additionally, @pid_callback (with
1996  * @pid_callback_data) will be called to inform about the PID of the
1997  * created process.
1998  *
1999  * If application launching occurs via some other mechanism (eg: D-Bus
2000  * activation) then @spawn_flags, @user_setup, @user_setup_data,
2001  * @pid_callback and @pid_callback_data are ignored.
2002  *
2003  * Returns: %TRUE on successful launch, %FALSE otherwise.
2004  */
2005 gboolean
2006 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo            *appinfo,
2007                                            GList                      *uris,
2008                                            GAppLaunchContext          *launch_context,
2009                                            GSpawnFlags                 spawn_flags,
2010                                            GSpawnChildSetupFunc        user_setup,
2011                                            gpointer                    user_setup_data,
2012                                            GDesktopAppLaunchCallback   pid_callback,
2013                                            gpointer                    pid_callback_data,
2014                                            GError                    **error)
2015 {
2016   return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
2017                                                   uris,
2018                                                   launch_context,
2019                                                   spawn_flags,
2020                                                   user_setup,
2021                                                   user_setup_data,
2022                                                   pid_callback,
2023                                                   pid_callback_data,
2024                                                   error);
2025 }
2026
2027 /* OnlyShowIn API support {{{2 */
2028
2029 /**
2030  * g_desktop_app_info_set_desktop_env:
2031  * @desktop_env: a string specifying what desktop this is
2032  *
2033  * Sets the name of the desktop that the application is running in.
2034  * This is used by g_app_info_should_show() and
2035  * g_desktop_app_info_get_show_in() to evaluate the
2036  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
2037  * desktop entry fields.
2038  *
2039  * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
2040  * Menu specification</ulink> recognizes the following:
2041  * <simplelist>
2042  *   <member>GNOME</member>
2043  *   <member>KDE</member>
2044  *   <member>ROX</member>
2045  *   <member>XFCE</member>
2046  *   <member>LXDE</member>
2047  *   <member>Unity</member>
2048  *   <member>Old</member>
2049  * </simplelist>
2050  *
2051  * Should be called only once; subsequent calls are ignored.
2052  */
2053 void
2054 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
2055 {
2056   G_LOCK (g_desktop_env);
2057   if (!g_desktop_env)
2058     g_desktop_env = g_strdup (desktop_env);
2059   G_UNLOCK (g_desktop_env);
2060 }
2061
2062 static gboolean
2063 g_desktop_app_info_should_show (GAppInfo *appinfo)
2064 {
2065   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2066
2067   if (info->nodisplay)
2068     return FALSE;
2069
2070   return g_desktop_app_info_get_show_in (info, NULL);
2071 }
2072
2073 /* mime types/default apps support {{{2 */
2074
2075 typedef enum {
2076   APP_DIR,
2077   MIMETYPE_DIR
2078 } DirType;
2079
2080 static char *
2081 ensure_dir (DirType   type,
2082             GError  **error)
2083 {
2084   char *path, *display_name;
2085   int errsv;
2086
2087   if (type == APP_DIR)
2088     path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
2089   else
2090     path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
2091
2092   errno = 0;
2093   if (g_mkdir_with_parents (path, 0700) == 0)
2094     return path;
2095
2096   errsv = errno;
2097   display_name = g_filename_display_name (path);
2098   if (type == APP_DIR)
2099     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
2100                  _("Can't create user application configuration folder %s: %s"),
2101                  display_name, g_strerror (errsv));
2102   else
2103     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
2104                  _("Can't create user MIME configuration folder %s: %s"),
2105                  display_name, g_strerror (errsv));
2106
2107   g_free (display_name);
2108   g_free (path);
2109
2110   return NULL;
2111 }
2112
2113 static gboolean
2114 update_mimeapps_list (const char  *desktop_id,
2115                       const char  *content_type,
2116                       UpdateMimeFlags flags,
2117                       GError     **error)
2118 {
2119   char *dirname, *filename, *string;
2120   GKeyFile *key_file;
2121   gboolean load_succeeded, res;
2122   char **old_list, **list;
2123   gsize length, data_size;
2124   char *data;
2125   int i, j, k;
2126   char **content_types;
2127
2128   /* Don't add both at start and end */
2129   g_assert (!((flags & UPDATE_MIME_SET_DEFAULT) &&
2130               (flags & UPDATE_MIME_SET_NON_DEFAULT)));
2131
2132   dirname = ensure_dir (APP_DIR, error);
2133   if (!dirname)
2134     return FALSE;
2135
2136   filename = g_build_filename (dirname, "mimeapps.list", NULL);
2137   g_free (dirname);
2138
2139   key_file = g_key_file_new ();
2140   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
2141   if (!load_succeeded ||
2142       (!g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP) &&
2143        !g_key_file_has_group (key_file, REMOVED_ASSOCIATIONS_GROUP) &&
2144        !g_key_file_has_group (key_file, DEFAULT_APPLICATIONS_GROUP)))
2145     {
2146       g_key_file_free (key_file);
2147       key_file = g_key_file_new ();
2148     }
2149
2150   if (content_type)
2151     {
2152       content_types = g_new (char *, 2);
2153       content_types[0] = g_strdup (content_type);
2154       content_types[1] = NULL;
2155     }
2156   else
2157     {
2158       content_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, NULL, NULL);
2159     }
2160
2161   for (k = 0; content_types && content_types[k]; k++)
2162     {
2163       /* set as default, if requested so */
2164       string = g_key_file_get_string (key_file,
2165                                       DEFAULT_APPLICATIONS_GROUP,
2166                                       content_types[k],
2167                                       NULL);
2168
2169       if (g_strcmp0 (string, desktop_id) != 0 &&
2170           (flags & UPDATE_MIME_SET_DEFAULT))
2171         {
2172           g_free (string);
2173           string = g_strdup (desktop_id);
2174
2175           /* add in the non-default list too, if it's not already there */
2176           flags |= UPDATE_MIME_SET_NON_DEFAULT;
2177         }
2178
2179       if (string == NULL || desktop_id == NULL)
2180         g_key_file_remove_key (key_file,
2181                                DEFAULT_APPLICATIONS_GROUP,
2182                                content_types[k],
2183                                NULL);
2184       else
2185         g_key_file_set_string (key_file,
2186                                DEFAULT_APPLICATIONS_GROUP,
2187                                content_types[k],
2188                                string);
2189
2190       g_free (string);
2191     }
2192
2193   if (content_type)
2194     {
2195       /* reuse the list from above */
2196     }
2197   else
2198     {
2199       g_strfreev (content_types);
2200       content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
2201     }
2202
2203   for (k = 0; content_types && content_types[k]; k++)
2204     {
2205       /* Add to the right place in the list */
2206
2207       length = 0;
2208       old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
2209                                              content_types[k], &length, NULL);
2210
2211       list = g_new (char *, 1 + length + 1);
2212
2213       i = 0;
2214
2215       /* if we're adding a last-used hint, just put the application in front of the list */
2216       if (flags & UPDATE_MIME_SET_LAST_USED)
2217         {
2218           /* avoid adding this again as non-default later */
2219           if (flags & UPDATE_MIME_SET_NON_DEFAULT)
2220             flags ^= UPDATE_MIME_SET_NON_DEFAULT;
2221
2222           list[i++] = g_strdup (desktop_id);
2223         }
2224
2225       if (old_list)
2226         {
2227           for (j = 0; old_list[j] != NULL; j++)
2228             {
2229               if (g_strcmp0 (old_list[j], desktop_id) != 0)
2230                 {
2231                   /* rewrite other entries if they're different from the new one */
2232                   list[i++] = g_strdup (old_list[j]);
2233                 }
2234               else if (flags & UPDATE_MIME_SET_NON_DEFAULT)
2235                 {
2236                   /* we encountered an old entry which is equal to the one we're adding as non-default,
2237                    * don't change its position in the list.
2238                    */
2239                   flags ^= UPDATE_MIME_SET_NON_DEFAULT;
2240                   list[i++] = g_strdup (old_list[j]);
2241                 }
2242             }
2243         }
2244
2245       /* add it at the end of the list */
2246       if (flags & UPDATE_MIME_SET_NON_DEFAULT)
2247         list[i++] = g_strdup (desktop_id);
2248
2249       list[i] = NULL;
2250
2251       g_strfreev (old_list);
2252
2253       if (list[0] == NULL || desktop_id == NULL)
2254         g_key_file_remove_key (key_file,
2255                                ADDED_ASSOCIATIONS_GROUP,
2256                                content_types[k],
2257                                NULL);
2258       else
2259         g_key_file_set_string_list (key_file,
2260                                     ADDED_ASSOCIATIONS_GROUP,
2261                                     content_types[k],
2262                                     (const char * const *)list, i);
2263
2264       g_strfreev (list);
2265     }
2266
2267   if (content_type)
2268     {
2269       /* reuse the list from above */
2270     }
2271   else
2272     {
2273       g_strfreev (content_types);
2274       content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
2275     }
2276
2277   for (k = 0; content_types && content_types[k]; k++)
2278     {
2279       /* Remove from removed associations group (unless remove) */
2280
2281       length = 0;
2282       old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
2283                                              content_types[k], &length, NULL);
2284
2285       list = g_new (char *, 1 + length + 1);
2286
2287       i = 0;
2288       if (flags & UPDATE_MIME_REMOVE)
2289         list[i++] = g_strdup (desktop_id);
2290       if (old_list)
2291         {
2292           for (j = 0; old_list[j] != NULL; j++)
2293             {
2294               if (g_strcmp0 (old_list[j], desktop_id) != 0)
2295                 list[i++] = g_strdup (old_list[j]);
2296             }
2297         }
2298       list[i] = NULL;
2299
2300       g_strfreev (old_list);
2301
2302       if (list[0] == NULL || desktop_id == NULL)
2303         g_key_file_remove_key (key_file,
2304                                REMOVED_ASSOCIATIONS_GROUP,
2305                                content_types[k],
2306                                NULL);
2307       else
2308         g_key_file_set_string_list (key_file,
2309                                     REMOVED_ASSOCIATIONS_GROUP,
2310                                     content_types[k],
2311                                     (const char * const *)list, i);
2312
2313       g_strfreev (list);
2314     }
2315
2316   g_strfreev (content_types);
2317
2318   data = g_key_file_to_data (key_file, &data_size, error);
2319   g_key_file_free (key_file);
2320
2321   res = g_file_set_contents (filename, data, data_size, error);
2322
2323   mime_info_cache_reload (NULL);
2324
2325   g_free (filename);
2326   g_free (data);
2327
2328   return res;
2329 }
2330
2331 static gboolean
2332 g_desktop_app_info_set_as_last_used_for_type (GAppInfo    *appinfo,
2333                                               const char  *content_type,
2334                                               GError     **error)
2335 {
2336   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2337
2338   if (!g_desktop_app_info_ensure_saved (info, error))
2339     return FALSE;
2340
2341   if (!info->desktop_id)
2342     {
2343       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2344                            _("Application information lacks an identifier"));
2345       return FALSE;
2346     }
2347
2348   /* both add support for the content type and set as last used */
2349   return update_mimeapps_list (info->desktop_id, content_type,
2350                                UPDATE_MIME_SET_NON_DEFAULT |
2351                                UPDATE_MIME_SET_LAST_USED,
2352                                error);
2353 }
2354
2355 static gboolean
2356 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
2357                                             const char  *content_type,
2358                                             GError     **error)
2359 {
2360   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2361
2362   if (!g_desktop_app_info_ensure_saved (info, error))
2363     return FALSE;
2364
2365   if (!info->desktop_id)
2366     {
2367       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2368                            _("Application information lacks an identifier"));
2369       return FALSE;
2370     }
2371
2372   return update_mimeapps_list (info->desktop_id, content_type,
2373                                UPDATE_MIME_SET_DEFAULT,
2374                                error);
2375 }
2376
2377 static void
2378 update_program_done (GPid     pid,
2379                      gint     status,
2380                      gpointer data)
2381 {
2382   /* Did the application exit correctly */
2383   if (g_spawn_check_exit_status (status, NULL))
2384     {
2385       /* Here we could clean out any caches in use */
2386     }
2387 }
2388
2389 static void
2390 run_update_command (char *command,
2391                     char *subdir)
2392 {
2393         char *argv[3] = {
2394                 NULL,
2395                 NULL,
2396                 NULL,
2397         };
2398         GPid pid = 0;
2399         GError *error = NULL;
2400
2401         argv[0] = command;
2402         argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
2403
2404         if (g_spawn_async ("/", argv,
2405                            NULL,       /* envp */
2406                            G_SPAWN_SEARCH_PATH |
2407                            G_SPAWN_STDOUT_TO_DEV_NULL |
2408                            G_SPAWN_STDERR_TO_DEV_NULL |
2409                            G_SPAWN_DO_NOT_REAP_CHILD,
2410                            NULL, NULL, /* No setup function */
2411                            &pid,
2412                            &error))
2413           g_child_watch_add (pid, update_program_done, NULL);
2414         else
2415           {
2416             /* If we get an error at this point, it's quite likely the user doesn't
2417              * have an installed copy of either 'update-mime-database' or
2418              * 'update-desktop-database'.  I don't think we want to popup an error
2419              * dialog at this point, so we just do a g_warning to give the user a
2420              * chance of debugging it.
2421              */
2422             g_warning ("%s", error->message);
2423           }
2424
2425         g_free (argv[1]);
2426 }
2427
2428 static gboolean
2429 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
2430                                                  const char  *extension,
2431                                                  GError     **error)
2432 {
2433   char *filename, *basename, *mimetype;
2434   char *dirname;
2435   gboolean res;
2436
2437   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
2438     return FALSE;
2439
2440   dirname = ensure_dir (MIMETYPE_DIR, error);
2441   if (!dirname)
2442     return FALSE;
2443
2444   basename = g_strdup_printf ("user-extension-%s.xml", extension);
2445   filename = g_build_filename (dirname, basename, NULL);
2446   g_free (basename);
2447   g_free (dirname);
2448
2449   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
2450
2451   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
2452     {
2453       char *contents;
2454
2455       contents =
2456         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2457                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
2458                          " <mime-type type=\"%s\">\n"
2459                          "  <comment>%s document</comment>\n"
2460                          "  <glob pattern=\"*.%s\"/>\n"
2461                          " </mime-type>\n"
2462                          "</mime-info>\n", mimetype, extension, extension);
2463
2464       g_file_set_contents (filename, contents, -1, NULL);
2465       g_free (contents);
2466
2467       run_update_command ("update-mime-database", "mime");
2468     }
2469   g_free (filename);
2470
2471   res = g_desktop_app_info_set_as_default_for_type (appinfo,
2472                                                     mimetype,
2473                                                     error);
2474
2475   g_free (mimetype);
2476
2477   return res;
2478 }
2479
2480 static gboolean
2481 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
2482                                       const char  *content_type,
2483                                       GError     **error)
2484 {
2485   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2486
2487   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
2488     return FALSE;
2489
2490   return update_mimeapps_list (info->desktop_id, content_type,
2491                                UPDATE_MIME_SET_NON_DEFAULT,
2492                                error);
2493 }
2494
2495 static gboolean
2496 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
2497 {
2498   return TRUE;
2499 }
2500
2501 static gboolean
2502 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
2503                                          const char  *content_type,
2504                                          GError     **error)
2505 {
2506   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2507
2508   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
2509     return FALSE;
2510
2511   return update_mimeapps_list (info->desktop_id, content_type,
2512                                UPDATE_MIME_REMOVE,
2513                                error);
2514 }
2515
2516 static const char **
2517 g_desktop_app_info_get_supported_types (GAppInfo *appinfo)
2518 {
2519   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2520
2521   return (const char**) info->mime_types;
2522 }
2523
2524 /* Saving and deleting {{{2 */
2525
2526 static gboolean
2527 g_desktop_app_info_ensure_saved (GDesktopAppInfo  *info,
2528                                  GError          **error)
2529 {
2530   GKeyFile *key_file;
2531   char *dirname;
2532   char *filename;
2533   char *data, *desktop_id;
2534   gsize data_size;
2535   int fd;
2536   gboolean res;
2537
2538   if (info->filename != NULL)
2539     return TRUE;
2540
2541   /* This is only used for object created with
2542    * g_app_info_create_from_commandline. All other
2543    * object should have a filename
2544    */
2545
2546   dirname = ensure_dir (APP_DIR, error);
2547   if (!dirname)
2548     return FALSE;
2549
2550   key_file = g_key_file_new ();
2551
2552   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2553                          "Encoding", "UTF-8");
2554   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2555                          G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
2556   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2557                          G_KEY_FILE_DESKTOP_KEY_TYPE,
2558                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
2559   if (info->terminal)
2560     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
2561                             G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
2562   if (info->nodisplay)
2563     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
2564                             G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
2565
2566   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2567                          G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
2568
2569   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2570                          G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
2571
2572   if (info->generic_name != NULL)
2573     g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2574                            GENERIC_NAME_KEY, info->generic_name);
2575
2576   if (info->fullname != NULL)
2577     g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2578                            FULL_NAME_KEY, info->fullname);
2579
2580   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
2581                          G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
2582
2583   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
2584                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
2585
2586   data = g_key_file_to_data (key_file, &data_size, NULL);
2587   g_key_file_free (key_file);
2588
2589   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
2590   filename = g_build_filename (dirname, desktop_id, NULL);
2591   g_free (desktop_id);
2592   g_free (dirname);
2593
2594   fd = g_mkstemp (filename);
2595   if (fd == -1)
2596     {
2597       char *display_name;
2598
2599       display_name = g_filename_display_name (filename);
2600       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2601                    _("Can't create user desktop file %s"), display_name);
2602       g_free (display_name);
2603       g_free (filename);
2604       g_free (data);
2605       return FALSE;
2606     }
2607
2608   desktop_id = g_path_get_basename (filename);
2609
2610   /* FIXME - actually handle error */
2611   (void) g_close (fd, NULL);
2612
2613   res = g_file_set_contents (filename, data, data_size, error);
2614   g_free (data);
2615   if (!res)
2616     {
2617       g_free (desktop_id);
2618       g_free (filename);
2619       return FALSE;
2620     }
2621
2622   info->filename = filename;
2623   info->desktop_id = desktop_id;
2624
2625   run_update_command ("update-desktop-database", "applications");
2626
2627   /* We just dropped a file in the user's desktop file directory.  Save
2628    * the monitor the bother of having to notice it and invalidate
2629    * immediately.
2630    *
2631    * This means that calls directly following this will be able to see
2632    * the results immediately.
2633    */
2634   desktop_file_dirs_invalidate_user ();
2635
2636   return TRUE;
2637 }
2638
2639 static gboolean
2640 g_desktop_app_info_can_delete (GAppInfo *appinfo)
2641 {
2642   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2643
2644   if (info->filename)
2645     {
2646       if (strstr (info->filename, "/userapp-"))
2647         return g_access (info->filename, W_OK) == 0;
2648     }
2649
2650   return FALSE;
2651 }
2652
2653 static gboolean
2654 g_desktop_app_info_delete (GAppInfo *appinfo)
2655 {
2656   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2657
2658   if (info->filename)
2659     {
2660       if (g_remove (info->filename) == 0)
2661         {
2662           update_mimeapps_list (info->desktop_id, NULL,
2663                                 UPDATE_MIME_NONE,
2664                                 NULL);
2665
2666           g_free (info->filename);
2667           info->filename = NULL;
2668           g_free (info->desktop_id);
2669           info->desktop_id = NULL;
2670
2671           return TRUE;
2672         }
2673     }
2674
2675   return FALSE;
2676 }
2677
2678 /* Create for commandline {{{2 */
2679 /**
2680  * g_app_info_create_from_commandline:
2681  * @commandline: the commandline to use
2682  * @application_name: (allow-none): the application name, or %NULL to use @commandline
2683  * @flags: flags that can specify details of the created #GAppInfo
2684  * @error: a #GError location to store the error occurring, %NULL to ignore.
2685  *
2686  * Creates a new #GAppInfo from the given information.
2687  *
2688  * Note that for @commandline, the quoting rules of the Exec key of the
2689  * <ulink url="http://freedesktop.org/Standards/desktop-entry-spec">freedesktop.org Desktop
2690  * Entry Specification</ulink> are applied. For example, if the @commandline contains
2691  * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
2692  * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
2693  *
2694  * Returns: (transfer full): new #GAppInfo for given command.
2695  **/
2696 GAppInfo *
2697 g_app_info_create_from_commandline (const char           *commandline,
2698                                     const char           *application_name,
2699                                     GAppInfoCreateFlags   flags,
2700                                     GError              **error)
2701 {
2702   char **split;
2703   char *basename;
2704   GDesktopAppInfo *info;
2705
2706   g_return_val_if_fail (commandline, NULL);
2707
2708   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
2709
2710   info->filename = NULL;
2711   info->desktop_id = NULL;
2712
2713   info->terminal = (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) != 0;
2714   info->startup_notify = (flags & G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION) != 0;
2715   info->hidden = FALSE;
2716   if ((flags & G_APP_INFO_CREATE_SUPPORTS_URIS) != 0)
2717     info->exec = g_strconcat (commandline, " %u", NULL);
2718   else
2719     info->exec = g_strconcat (commandline, " %f", NULL);
2720   info->nodisplay = TRUE;
2721   info->binary = binary_from_exec (info->exec);
2722
2723   if (application_name)
2724     info->name = g_strdup (application_name);
2725   else
2726     {
2727       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
2728       split = g_strsplit (commandline, " ", 2);
2729       basename = split[0] ? g_path_get_basename (split[0]) : NULL;
2730       g_strfreev (split);
2731       info->name = basename;
2732       if (info->name == NULL)
2733         info->name = g_strdup ("custom");
2734     }
2735   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
2736
2737   return G_APP_INFO (info);
2738 }
2739
2740 /* GAppInfo interface init */
2741
2742 static void
2743 g_desktop_app_info_iface_init (GAppInfoIface *iface)
2744 {
2745   iface->dup = g_desktop_app_info_dup;
2746   iface->equal = g_desktop_app_info_equal;
2747   iface->get_id = g_desktop_app_info_get_id;
2748   iface->get_name = g_desktop_app_info_get_name;
2749   iface->get_description = g_desktop_app_info_get_description;
2750   iface->get_executable = g_desktop_app_info_get_executable;
2751   iface->get_icon = g_desktop_app_info_get_icon;
2752   iface->launch = g_desktop_app_info_launch;
2753   iface->supports_uris = g_desktop_app_info_supports_uris;
2754   iface->supports_files = g_desktop_app_info_supports_files;
2755   iface->launch_uris = g_desktop_app_info_launch_uris;
2756   iface->should_show = g_desktop_app_info_should_show;
2757   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
2758   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
2759   iface->add_supports_type = g_desktop_app_info_add_supports_type;
2760   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
2761   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
2762   iface->can_delete = g_desktop_app_info_can_delete;
2763   iface->do_delete = g_desktop_app_info_delete;
2764   iface->get_commandline = g_desktop_app_info_get_commandline;
2765   iface->get_display_name = g_desktop_app_info_get_display_name;
2766   iface->set_as_last_used_for_type = g_desktop_app_info_set_as_last_used_for_type;
2767   iface->get_supported_types = g_desktop_app_info_get_supported_types;
2768 }
2769
2770 /* Recommended applications {{{2 */
2771
2772 static gboolean
2773 app_info_in_list (GAppInfo *info,
2774                   GList    *list)
2775 {
2776   while (list != NULL)
2777     {
2778       if (g_app_info_equal (info, list->data))
2779         return TRUE;
2780       list = list->next;
2781     }
2782   return FALSE;
2783 }
2784
2785 /**
2786  * g_app_info_get_recommended_for_type:
2787  * @content_type: the content type to find a #GAppInfo for
2788  *
2789  * Gets a list of recommended #GAppInfos for a given content type, i.e.
2790  * those applications which claim to support the given content type exactly,
2791  * and not by MIME type subclassing.
2792  * Note that the first application of the list is the last used one, i.e.
2793  * the last one for which g_app_info_set_as_last_used_for_type() has been
2794  * called.
2795  *
2796  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
2797  *     for given @content_type or %NULL on error.
2798  *
2799  * Since: 2.28
2800  **/
2801 GList *
2802 g_app_info_get_recommended_for_type (const gchar *content_type)
2803 {
2804   GList *desktop_entries, *l;
2805   GList *infos;
2806   GDesktopAppInfo *info;
2807
2808   g_return_val_if_fail (content_type != NULL, NULL);
2809
2810   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, FALSE, NULL);
2811
2812   infos = NULL;
2813   for (l = desktop_entries; l != NULL; l = l->next)
2814     {
2815       char *desktop_entry = l->data;
2816
2817       info = g_desktop_app_info_new (desktop_entry);
2818       if (info)
2819         {
2820           if (app_info_in_list (G_APP_INFO (info), infos))
2821             g_object_unref (info);
2822           else
2823             infos = g_list_prepend (infos, info);
2824         }
2825       g_free (desktop_entry);
2826     }
2827
2828   g_list_free (desktop_entries);
2829
2830   return g_list_reverse (infos);
2831 }
2832
2833 /**
2834  * g_app_info_get_fallback_for_type:
2835  * @content_type: the content type to find a #GAppInfo for
2836  *
2837  * Gets a list of fallback #GAppInfos for a given content type, i.e.
2838  * those applications which claim to support the given content type
2839  * by MIME type subclassing and not directly.
2840  *
2841  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
2842  *     for given @content_type or %NULL on error.
2843  *
2844  * Since: 2.28
2845  **/
2846 GList *
2847 g_app_info_get_fallback_for_type (const gchar *content_type)
2848 {
2849   GList *desktop_entries, *l;
2850   GList *infos, *recommended_infos;
2851   GDesktopAppInfo *info;
2852
2853   g_return_val_if_fail (content_type != NULL, NULL);
2854
2855   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE, NULL);
2856   recommended_infos = g_app_info_get_recommended_for_type (content_type);
2857
2858   infos = NULL;
2859   for (l = desktop_entries; l != NULL; l = l->next)
2860     {
2861       char *desktop_entry = l->data;
2862
2863       info = g_desktop_app_info_new (desktop_entry);
2864       if (info)
2865         {
2866           if (app_info_in_list (G_APP_INFO (info), infos) ||
2867               app_info_in_list (G_APP_INFO (info), recommended_infos))
2868             g_object_unref (info);
2869           else
2870             infos = g_list_prepend (infos, info);
2871         }
2872       g_free (desktop_entry);
2873     }
2874
2875   g_list_free (desktop_entries);
2876   g_list_free_full (recommended_infos, g_object_unref);
2877
2878   return g_list_reverse (infos);
2879 }
2880
2881 /**
2882  * g_app_info_get_all_for_type:
2883  * @content_type: the content type to find a #GAppInfo for
2884  *
2885  * Gets a list of all #GAppInfos for a given content type,
2886  * including the recommended and fallback #GAppInfos. See
2887  * g_app_info_get_recommended_for_type() and
2888  * g_app_info_get_fallback_for_type().
2889  *
2890  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
2891  *     for given @content_type or %NULL on error.
2892  **/
2893 GList *
2894 g_app_info_get_all_for_type (const char *content_type)
2895 {
2896   GList *desktop_entries, *l;
2897   GList *infos;
2898   char *user_default = NULL;
2899   GDesktopAppInfo *info;
2900
2901   g_return_val_if_fail (content_type != NULL, NULL);
2902
2903   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE, &user_default);
2904   infos = NULL;
2905
2906   /* put the user default in front of the list, for compatibility */
2907   if (user_default != NULL)
2908     {
2909       info = g_desktop_app_info_new (user_default);
2910
2911       if (info != NULL)
2912         infos = g_list_prepend (infos, info);
2913     }
2914
2915   g_free (user_default);
2916
2917   for (l = desktop_entries; l != NULL; l = l->next)
2918     {
2919       char *desktop_entry = l->data;
2920
2921       info = g_desktop_app_info_new (desktop_entry);
2922       if (info)
2923         {
2924           if (app_info_in_list (G_APP_INFO (info), infos))
2925             g_object_unref (info);
2926           else
2927             infos = g_list_prepend (infos, info);
2928         }
2929       g_free (desktop_entry);
2930     }
2931
2932   g_list_free (desktop_entries);
2933
2934   return g_list_reverse (infos);
2935 }
2936
2937 /**
2938  * g_app_info_reset_type_associations:
2939  * @content_type: a content type
2940  *
2941  * Removes all changes to the type associations done by
2942  * g_app_info_set_as_default_for_type(),
2943  * g_app_info_set_as_default_for_extension(),
2944  * g_app_info_add_supports_type() or
2945  * g_app_info_remove_supports_type().
2946  *
2947  * Since: 2.20
2948  */
2949 void
2950 g_app_info_reset_type_associations (const char *content_type)
2951 {
2952   update_mimeapps_list (NULL, content_type,
2953                         UPDATE_MIME_NONE,
2954                         NULL);
2955 }
2956
2957 /**
2958  * g_app_info_get_default_for_type:
2959  * @content_type: the content type to find a #GAppInfo for
2960  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
2961  *     support URIs
2962  *
2963  * Gets the default #GAppInfo for a given content type.
2964  *
2965  * Returns: (transfer full): #GAppInfo for given @content_type or
2966  *     %NULL on error.
2967  */
2968 GAppInfo *
2969 g_app_info_get_default_for_type (const char *content_type,
2970                                  gboolean    must_support_uris)
2971 {
2972   GList *desktop_entries, *l;
2973   char *user_default = NULL;
2974   GAppInfo *info;
2975
2976   g_return_val_if_fail (content_type != NULL, NULL);
2977
2978   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL, TRUE, &user_default);
2979
2980   info = NULL;
2981
2982   if (user_default != NULL)
2983     {
2984       info = (GAppInfo *) g_desktop_app_info_new (user_default);
2985
2986       if (info)
2987         {
2988           if (must_support_uris && !g_app_info_supports_uris (info))
2989             {
2990               g_object_unref (info);
2991               info = NULL;
2992             }
2993         }
2994     }
2995
2996   g_free (user_default);
2997
2998   if (info != NULL)
2999     {
3000       g_list_free_full (desktop_entries, g_free);
3001       return info;
3002     }
3003
3004   /* pick the first from the other list that matches our URI
3005    * requirements.
3006    */
3007   for (l = desktop_entries; l != NULL; l = l->next)
3008     {
3009       char *desktop_entry = l->data;
3010
3011       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
3012       if (info)
3013         {
3014           if (must_support_uris && !g_app_info_supports_uris (info))
3015             {
3016               g_object_unref (info);
3017               info = NULL;
3018             }
3019           else
3020             break;
3021         }
3022     }
3023
3024   g_list_free_full (desktop_entries, g_free);
3025
3026   return info;
3027 }
3028
3029 /**
3030  * g_app_info_get_default_for_uri_scheme:
3031  * @uri_scheme: a string containing a URI scheme.
3032  *
3033  * Gets the default application for handling URIs with
3034  * the given URI scheme. A URI scheme is the initial part
3035  * of the URI, up to but not including the ':', e.g. "http",
3036  * "ftp" or "sip".
3037  *
3038  * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
3039  */
3040 GAppInfo *
3041 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
3042 {
3043   GAppInfo *app_info;
3044   char *content_type, *scheme_down;
3045
3046   scheme_down = g_ascii_strdown (uri_scheme, -1);
3047   content_type = g_strdup_printf ("x-scheme-handler/%s", scheme_down);
3048   g_free (scheme_down);
3049   app_info = g_app_info_get_default_for_type (content_type, FALSE);
3050   g_free (content_type);
3051
3052   return app_info;
3053 }
3054
3055 /* "Get all" API {{{2 */
3056
3057 /**
3058  * g_app_info_get_all:
3059  *
3060  * Gets a list of all of the applications currently registered
3061  * on this system.
3062  *
3063  * For desktop files, this includes applications that have
3064  * <literal>NoDisplay=true</literal> set or are excluded from
3065  * display by means of <literal>OnlyShowIn</literal> or
3066  * <literal>NotShowIn</literal>. See g_app_info_should_show().
3067  * The returned list does not include applications which have
3068  * the <literal>Hidden</literal> key set.
3069  *
3070  * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfo<!---->s.
3071  **/
3072 GList *
3073 g_app_info_get_all (void)
3074 {
3075   GHashTable *apps;
3076   GHashTableIter iter;
3077   gpointer value;
3078   int i;
3079   GList *infos;
3080
3081   apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3082
3083   desktop_file_dirs_lock ();
3084
3085   for (i = 0; i < n_desktop_file_dirs; i++)
3086     desktop_file_dir_get_all (&desktop_file_dirs[i], apps);
3087
3088   desktop_file_dirs_unlock ();
3089
3090   infos = NULL;
3091   g_hash_table_iter_init (&iter, apps);
3092   while (g_hash_table_iter_next (&iter, NULL, &value))
3093     {
3094       if (value)
3095         infos = g_list_prepend (infos, value);
3096     }
3097
3098   g_hash_table_destroy (apps);
3099
3100   return infos;
3101 }
3102
3103 /* Caching of mimeinfo.cache and defaults.list files {{{2 */
3104
3105 typedef struct {
3106   char *path;
3107   GHashTable *mime_info_cache_map;
3108   GHashTable *defaults_list_map;
3109   GHashTable *mimeapps_list_added_map;
3110   GHashTable *mimeapps_list_removed_map;
3111   GHashTable *mimeapps_list_defaults_map;
3112   time_t mime_info_cache_timestamp;
3113   time_t defaults_list_timestamp;
3114   time_t mimeapps_list_timestamp;
3115 } MimeInfoCacheDir;
3116
3117 typedef struct {
3118   GList *dirs;                       /* mimeinfo.cache and defaults.list */
3119   time_t last_stat_time;
3120 } MimeInfoCache;
3121
3122 static MimeInfoCache *mime_info_cache = NULL;
3123 G_LOCK_DEFINE_STATIC (mime_info_cache);
3124
3125 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
3126                                                      const char        *mime_type,
3127                                                      char             **new_desktop_file_ids);
3128
3129 static MimeInfoCache * mime_info_cache_new (void);
3130
3131 static void
3132 destroy_info_cache_value (gpointer  key,
3133                           GList    *value,
3134                           gpointer  data)
3135 {
3136   g_list_free_full (value, g_free);
3137 }
3138
3139 static void
3140 destroy_info_cache_map (GHashTable *info_cache_map)
3141 {
3142   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
3143   g_hash_table_destroy (info_cache_map);
3144 }
3145
3146 static gboolean
3147 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
3148                                  const char       *cache_file,
3149                                  time_t           *timestamp)
3150 {
3151   struct stat buf;
3152   char *filename;
3153
3154   filename = g_build_filename (dir->path, cache_file, NULL);
3155
3156   if (g_stat (filename, &buf) < 0)
3157     {
3158       g_free (filename);
3159       return TRUE;
3160     }
3161   g_free (filename);
3162
3163   if (buf.st_mtime != *timestamp)
3164     return TRUE;
3165
3166   return FALSE;
3167 }
3168
3169 /* Call with lock held */
3170 static void
3171 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
3172 {
3173   GError *load_error;
3174   GKeyFile *key_file;
3175   gchar *filename, **mime_types;
3176   int i;
3177   struct stat buf;
3178
3179   load_error = NULL;
3180   mime_types = NULL;
3181
3182   if (dir->mime_info_cache_map != NULL &&
3183       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
3184                                         &dir->mime_info_cache_timestamp))
3185     return;
3186
3187   if (dir->mime_info_cache_map != NULL)
3188     destroy_info_cache_map (dir->mime_info_cache_map);
3189
3190   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
3191                                                     (GDestroyNotify) g_free,
3192                                                     NULL);
3193
3194   key_file = g_key_file_new ();
3195
3196   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
3197
3198   if (g_stat (filename, &buf) < 0)
3199     goto error;
3200
3201   dir->mime_info_cache_timestamp = buf.st_mtime;
3202
3203   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
3204
3205   g_free (filename);
3206   filename = NULL;
3207
3208   if (load_error != NULL)
3209     goto error;
3210
3211   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
3212                                     NULL, &load_error);
3213
3214   if (load_error != NULL)
3215     goto error;
3216
3217   for (i = 0; mime_types[i] != NULL; i++)
3218     {
3219       gchar **desktop_file_ids;
3220       char *unaliased_type;
3221       desktop_file_ids = g_key_file_get_string_list (key_file,
3222                                                      MIME_CACHE_GROUP,
3223                                                      mime_types[i],
3224                                                      NULL,
3225                                                      NULL);
3226
3227       if (desktop_file_ids == NULL)
3228         continue;
3229
3230       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
3231       mime_info_cache_dir_add_desktop_entries (dir,
3232                                                unaliased_type,
3233                                                desktop_file_ids);
3234       g_free (unaliased_type);
3235
3236       g_strfreev (desktop_file_ids);
3237     }
3238
3239   g_strfreev (mime_types);
3240   g_key_file_free (key_file);
3241
3242   return;
3243  error:
3244   g_free (filename);
3245   g_key_file_free (key_file);
3246
3247   if (mime_types != NULL)
3248     g_strfreev (mime_types);
3249
3250   if (load_error)
3251     g_error_free (load_error);
3252 }
3253
3254 static void
3255 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
3256 {
3257   GKeyFile *key_file;
3258   GError *load_error;
3259   gchar *filename, **mime_types;
3260   char *unaliased_type;
3261   char **desktop_file_ids;
3262   int i;
3263   struct stat buf;
3264
3265   load_error = NULL;
3266   mime_types = NULL;
3267
3268   if (dir->defaults_list_map != NULL &&
3269       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
3270                                         &dir->defaults_list_timestamp))
3271     return;
3272
3273   if (dir->defaults_list_map != NULL)
3274     g_hash_table_destroy (dir->defaults_list_map);
3275   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
3276                                                   g_free, (GDestroyNotify)g_strfreev);
3277
3278
3279   key_file = g_key_file_new ();
3280
3281   filename = g_build_filename (dir->path, "defaults.list", NULL);
3282   if (g_stat (filename, &buf) < 0)
3283     goto error;
3284
3285   dir->defaults_list_timestamp = buf.st_mtime;
3286
3287   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
3288   g_free (filename);
3289   filename = NULL;
3290
3291   if (load_error != NULL)
3292     goto error;
3293
3294   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
3295                                     NULL, NULL);
3296   if (mime_types != NULL)
3297     {
3298       for (i = 0; mime_types[i] != NULL; i++)
3299         {
3300           desktop_file_ids = g_key_file_get_string_list (key_file,
3301                                                          DEFAULT_APPLICATIONS_GROUP,
3302                                                          mime_types[i],
3303                                                          NULL,
3304                                                          NULL);
3305           if (desktop_file_ids == NULL)
3306             continue;
3307
3308           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
3309           g_hash_table_replace (dir->defaults_list_map,
3310                                 unaliased_type,
3311                                 desktop_file_ids);
3312         }
3313
3314       g_strfreev (mime_types);
3315     }
3316
3317   g_key_file_free (key_file);
3318   return;
3319
3320  error:
3321   g_free (filename);
3322   g_key_file_free (key_file);
3323
3324   if (mime_types != NULL)
3325     g_strfreev (mime_types);
3326
3327   if (load_error)
3328     g_error_free (load_error);
3329 }
3330
3331 static void
3332 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
3333 {
3334   GKeyFile *key_file;
3335   GError *load_error;
3336   gchar *filename, **mime_types;
3337   char *unaliased_type;
3338   char **desktop_file_ids;
3339   char *desktop_id;
3340   int i;
3341   struct stat buf;
3342
3343   load_error = NULL;
3344   mime_types = NULL;
3345
3346   if (dir->mimeapps_list_added_map != NULL &&
3347       !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
3348                                         &dir->mimeapps_list_timestamp))
3349     return;
3350
3351   if (dir->mimeapps_list_added_map != NULL)
3352     g_hash_table_destroy (dir->mimeapps_list_added_map);
3353   dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
3354                                                         g_free, (GDestroyNotify)g_strfreev);
3355
3356   if (dir->mimeapps_list_removed_map != NULL)
3357     g_hash_table_destroy (dir->mimeapps_list_removed_map);
3358   dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
3359                                                           g_free, (GDestroyNotify)g_strfreev);
3360
3361   if (dir->mimeapps_list_defaults_map != NULL)
3362     g_hash_table_destroy (dir->mimeapps_list_defaults_map);
3363   dir->mimeapps_list_defaults_map = g_hash_table_new_full (g_str_hash, g_str_equal,
3364                                                            g_free, g_free);
3365
3366   key_file = g_key_file_new ();
3367
3368   filename = g_build_filename (dir->path, "mimeapps.list", NULL);
3369   if (g_stat (filename, &buf) < 0)
3370     goto error;
3371
3372   dir->mimeapps_list_timestamp = buf.st_mtime;
3373
3374   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
3375   g_free (filename);
3376   filename = NULL;
3377
3378   if (load_error != NULL)
3379     goto error;
3380
3381   mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
3382                                     NULL, NULL);
3383   if (mime_types != NULL)
3384     {
3385       for (i = 0; mime_types[i] != NULL; i++)
3386         {
3387           desktop_file_ids = g_key_file_get_string_list (key_file,
3388                                                          ADDED_ASSOCIATIONS_GROUP,
3389                                                          mime_types[i],
3390                                                          NULL,
3391                                                          NULL);
3392           if (desktop_file_ids == NULL)
3393             continue;
3394
3395           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
3396           g_hash_table_replace (dir->mimeapps_list_added_map,
3397                                 unaliased_type,
3398                                 desktop_file_ids);
3399         }
3400
3401       g_strfreev (mime_types);
3402     }
3403
3404   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
3405                                     NULL, NULL);
3406   if (mime_types != NULL)
3407     {
3408       for (i = 0; mime_types[i] != NULL; i++)
3409         {
3410           desktop_file_ids = g_key_file_get_string_list (key_file,
3411                                                          REMOVED_ASSOCIATIONS_GROUP,
3412                                                          mime_types[i],
3413                                                          NULL,
3414                                                          NULL);
3415           if (desktop_file_ids == NULL)
3416             continue;
3417
3418           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
3419           g_hash_table_replace (dir->mimeapps_list_removed_map,
3420                                 unaliased_type,
3421                                 desktop_file_ids);
3422         }
3423
3424       g_strfreev (mime_types);
3425     }
3426
3427   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
3428                                     NULL, NULL);
3429   if (mime_types != NULL)
3430     {
3431       for (i = 0; mime_types[i] != NULL; i++)
3432         {
3433           desktop_id = g_key_file_get_string (key_file,
3434                                               DEFAULT_APPLICATIONS_GROUP,
3435                                               mime_types[i],
3436                                               NULL);
3437           if (desktop_id == NULL)
3438             continue;
3439
3440           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
3441           g_hash_table_replace (dir->mimeapps_list_defaults_map,
3442                                 unaliased_type,
3443                                 desktop_id);
3444         }
3445
3446       g_strfreev (mime_types);
3447     }
3448
3449   g_key_file_free (key_file);
3450   return;
3451
3452  error:
3453   g_free (filename);
3454   g_key_file_free (key_file);
3455
3456   if (mime_types != NULL)
3457     g_strfreev (mime_types);
3458
3459   if (load_error)
3460     g_error_free (load_error);
3461 }
3462
3463 static MimeInfoCacheDir *
3464 mime_info_cache_dir_new (const char *path)
3465 {
3466   MimeInfoCacheDir *dir;
3467
3468   dir = g_new0 (MimeInfoCacheDir, 1);
3469   dir->path = g_strdup (path);
3470
3471   return dir;
3472 }
3473
3474 static void
3475 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
3476 {
3477   if (dir == NULL)
3478     return;
3479
3480   if (dir->mime_info_cache_map != NULL)
3481     {
3482       destroy_info_cache_map (dir->mime_info_cache_map);
3483       dir->mime_info_cache_map = NULL;
3484
3485   }
3486
3487   if (dir->defaults_list_map != NULL)
3488     {
3489       g_hash_table_destroy (dir->defaults_list_map);
3490       dir->defaults_list_map = NULL;
3491     }
3492
3493   if (dir->mimeapps_list_added_map != NULL)
3494     {
3495       g_hash_table_destroy (dir->mimeapps_list_added_map);
3496       dir->mimeapps_list_added_map = NULL;
3497     }
3498
3499   if (dir->mimeapps_list_removed_map != NULL)
3500     {
3501       g_hash_table_destroy (dir->mimeapps_list_removed_map);
3502       dir->mimeapps_list_removed_map = NULL;
3503     }
3504
3505   if (dir->mimeapps_list_defaults_map != NULL)
3506     {
3507       g_hash_table_destroy (dir->mimeapps_list_defaults_map);
3508       dir->mimeapps_list_defaults_map = NULL;
3509     }
3510
3511   g_free (dir);
3512 }
3513
3514 static void
3515 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
3516                                          const char        *mime_type,
3517                                          char             **new_desktop_file_ids)
3518 {
3519   GList *desktop_file_ids;
3520   int i;
3521
3522   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
3523                                           mime_type);
3524
3525   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
3526     {
3527       if (!g_list_find_custom (desktop_file_ids, new_desktop_file_ids[i], (GCompareFunc) strcmp))
3528         desktop_file_ids = g_list_append (desktop_file_ids,
3529                                           g_strdup (new_desktop_file_ids[i]));
3530     }
3531
3532   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
3533 }
3534
3535 static void
3536 mime_info_cache_init_dir_lists (void)
3537 {
3538   int i;
3539
3540   mime_info_cache = mime_info_cache_new ();
3541
3542   desktop_file_dirs_refresh ();
3543
3544   for (i = 0; i < n_desktop_file_dirs; i++)
3545     {
3546       MimeInfoCacheDir *dir;
3547
3548       dir = mime_info_cache_dir_new (desktop_file_dirs[i].path);
3549
3550       if (dir != NULL)
3551         {
3552           mime_info_cache_dir_init (dir);
3553           mime_info_cache_dir_init_defaults_list (dir);
3554           mime_info_cache_dir_init_mimeapps_list (dir);
3555
3556           mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
3557         }
3558     }
3559 }
3560
3561 static void
3562 mime_info_cache_update_dir_lists (void)
3563 {
3564   GList *tmp;
3565
3566   tmp = mime_info_cache->dirs;
3567
3568   while (tmp != NULL)
3569     {
3570       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
3571
3572       /* No need to do this if we had file monitors... */
3573       mime_info_cache_dir_init (dir);
3574       mime_info_cache_dir_init_defaults_list (dir);
3575       mime_info_cache_dir_init_mimeapps_list (dir);
3576
3577       tmp = tmp->next;
3578     }
3579 }
3580
3581 static void
3582 mime_info_cache_init (void)
3583 {
3584   G_LOCK (mime_info_cache);
3585   if (mime_info_cache == NULL)
3586     mime_info_cache_init_dir_lists ();
3587   else
3588     {
3589       time_t now;
3590
3591       time (&now);
3592       if (now >= mime_info_cache->last_stat_time + 10)
3593         {
3594           mime_info_cache_update_dir_lists ();
3595           mime_info_cache->last_stat_time = now;
3596         }
3597     }
3598
3599   G_UNLOCK (mime_info_cache);
3600 }
3601
3602 static MimeInfoCache *
3603 mime_info_cache_new (void)
3604 {
3605   MimeInfoCache *cache;
3606
3607   cache = g_new0 (MimeInfoCache, 1);
3608
3609   return cache;
3610 }
3611
3612 static void
3613 mime_info_cache_free (MimeInfoCache *cache)
3614 {
3615   if (cache == NULL)
3616     return;
3617
3618   g_list_free_full (cache->dirs, (GDestroyNotify) mime_info_cache_dir_free);
3619   g_free (cache);
3620 }
3621
3622 /**
3623  * mime_info_cache_reload:
3624  * @dir: directory path which needs reloading.
3625  *
3626  * Reload the mime information for the @dir.
3627  */
3628 static void
3629 mime_info_cache_reload (const char *dir)
3630 {
3631   /* FIXME: just reload the dir that needs reloading,
3632    * don't blow the whole cache
3633    */
3634   if (mime_info_cache != NULL)
3635     {
3636       G_LOCK (mime_info_cache);
3637       mime_info_cache_free (mime_info_cache);
3638       mime_info_cache = NULL;
3639       G_UNLOCK (mime_info_cache);
3640     }
3641 }
3642
3643 static GList *
3644 append_desktop_entry (GList      *list,
3645                       const char *desktop_entry,
3646                       GList      *removed_entries)
3647 {
3648   /* Add if not already in list, and valid */
3649   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
3650       !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
3651     list = g_list_prepend (list, g_strdup (desktop_entry));
3652
3653   return list;
3654 }
3655
3656 /**
3657  * get_all_desktop_entries_for_mime_type:
3658  * @mime_type: a mime type.
3659  * @except: NULL or a strv list
3660  *
3661  * Returns all the desktop ids for @mime_type. The desktop files
3662  * are listed in an order so that default applications are listed before
3663  * non-default ones, and handlers for inherited mimetypes are listed
3664  * after the base ones.
3665  *
3666  * Optionally doesn't list the desktop ids given in the @except
3667  *
3668  * Return value: a #GList containing the desktop ids which claim
3669  *    to handle @mime_type.
3670  */
3671 static GList *
3672 get_all_desktop_entries_for_mime_type (const char  *base_mime_type,
3673                                        const char **except,
3674                                        gboolean     include_fallback,
3675                                        char       **explicit_default)
3676 {
3677   GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
3678   MimeInfoCacheDir *dir;
3679   char *mime_type, *default_entry = NULL;
3680   char *old_default_entry = NULL;
3681   const char *entry;
3682   char **mime_types;
3683   char **default_entries;
3684   char **removed_associations;
3685   gboolean already_found_handler;
3686   int i, j, k;
3687   GPtrArray *array;
3688   char **anc;
3689
3690   mime_info_cache_init ();
3691
3692   if (include_fallback)
3693     {
3694       /* collect all ancestors */
3695       mime_types = _g_unix_content_type_get_parents (base_mime_type);
3696       array = g_ptr_array_new ();
3697       for (i = 0; mime_types[i]; i++)
3698         g_ptr_array_add (array, mime_types[i]);
3699       g_free (mime_types);
3700       for (i = 0; i < array->len; i++)
3701         {
3702           anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
3703           for (j = 0; anc[j]; j++)
3704             {
3705               for (k = 0; k < array->len; k++)
3706                 {
3707                   if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
3708                     break;
3709                 }
3710               if (k == array->len) /* not found */
3711                 g_ptr_array_add (array, g_strdup (anc[j]));
3712             }
3713           g_strfreev (anc);
3714         }
3715       g_ptr_array_add (array, NULL);
3716       mime_types = (char **)g_ptr_array_free (array, FALSE);
3717     }
3718   else
3719     {
3720       mime_types = g_malloc0 (2 * sizeof (gchar *));
3721       mime_types[0] = g_strdup (base_mime_type);
3722       mime_types[1] = NULL;
3723     }
3724
3725   G_LOCK (mime_info_cache);
3726
3727   removed_entries = NULL;
3728   desktop_entries = NULL;
3729
3730   for (i = 0; except != NULL && except[i] != NULL; i++)
3731     removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
3732
3733   for (i = 0; mime_types[i] != NULL; i++)
3734     {
3735       mime_type = mime_types[i];
3736
3737       /* This is true if we already found a handler for a more specific
3738          mimetype. If its set we ignore any defaults for the less specific
3739          mimetypes. */
3740       already_found_handler = (desktop_entries != NULL);
3741
3742       /* Go through all apps listed in user and system dirs */
3743       for (dir_list = mime_info_cache->dirs;
3744            dir_list != NULL;
3745            dir_list = dir_list->next)
3746         {
3747           dir = dir_list->data;
3748
3749           /* Pick the explicit default application if we got no result earlier
3750            * (ie, for more specific mime types)
3751            */
3752           if (!already_found_handler)
3753             {
3754               entry = g_hash_table_lookup (dir->mimeapps_list_defaults_map, mime_type);
3755
3756               if (entry != NULL)
3757                 {
3758                   /* Save the default entry if it's the first one we encounter */
3759                   if (default_entry == NULL)
3760                     default_entry = g_strdup (entry);
3761                 }
3762             }
3763
3764           /* Then added associations from mimeapps.list */
3765           default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
3766           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
3767             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
3768
3769           /* Then removed associations from mimeapps.list */
3770           removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
3771           for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
3772             removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
3773
3774           /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
3775           default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
3776           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
3777             {
3778               if (default_entry == NULL && old_default_entry == NULL && !already_found_handler)
3779                 old_default_entry = g_strdup (default_entries[j]);
3780
3781               desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
3782             }
3783         }
3784
3785       /* Go through all entries that support the mimetype */
3786       for (dir_list = mime_info_cache->dirs;
3787            dir_list != NULL;
3788            dir_list = dir_list->next)
3789         {
3790           dir = dir_list->data;
3791
3792           list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
3793           for (tmp = list; tmp != NULL; tmp = tmp->next)
3794             desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
3795         }
3796     }
3797
3798   G_UNLOCK (mime_info_cache);
3799
3800   g_strfreev (mime_types);
3801
3802   /* If we have no default from mimeapps.list, take it from
3803    * defaults.list intead.
3804    *
3805    * If we do have a default from mimeapps.list, free any one that came
3806    * from defaults.list.
3807    */
3808   if (default_entry == NULL)
3809     default_entry = old_default_entry;
3810   else
3811     g_free (old_default_entry);
3812
3813   if (explicit_default != NULL)
3814     *explicit_default = default_entry;
3815   else
3816     g_free (default_entry);
3817
3818   g_list_free_full (removed_entries, g_free);
3819
3820   desktop_entries = g_list_reverse (desktop_entries);
3821
3822   return desktop_entries;
3823 }
3824
3825 /* GDesktopAppInfoLookup interface {{{2 */
3826
3827 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3828
3829 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface;
3830 G_DEFINE_INTERFACE (GDesktopAppInfoLookup, g_desktop_app_info_lookup, G_TYPE_OBJECT)
3831
3832 static void
3833 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface)
3834 {
3835 }
3836
3837 /* "Get for mime type" APIs {{{2 */
3838
3839 /**
3840  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
3841  * @lookup: a #GDesktopAppInfoLookup
3842  * @uri_scheme: a string containing a URI scheme.
3843  *
3844  * Gets the default application for launching applications
3845  * using this URI scheme for a particular GDesktopAppInfoLookup
3846  * implementation.
3847  *
3848  * The GDesktopAppInfoLookup interface and this function is used
3849  * to implement g_app_info_get_default_for_uri_scheme() backends
3850  * in a GIO module. There is no reason for applications to use it
3851  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
3852  *
3853  * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
3854  *
3855  * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
3856  */
3857 GAppInfo *
3858 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
3859                                                       const char            *uri_scheme)
3860 {
3861   GDesktopAppInfoLookupIface *iface;
3862
3863   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
3864
3865   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
3866
3867   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
3868 }
3869
3870 G_GNUC_END_IGNORE_DEPRECATIONS
3871
3872 /* Misc getter APIs {{{2 */
3873
3874 /**
3875  * g_desktop_app_info_get_startup_wm_class:
3876  * @info: a #GDesktopAppInfo that supports startup notify
3877  *
3878  * Retrieves the StartupWMClass field from @info. This represents the
3879  * WM_CLASS property of the main window of the application, if launched
3880  * through @info.
3881  *
3882  * Returns: (transfer none): the startup WM class, or %NULL if none is set
3883  * in the desktop file.
3884  *
3885  * Since: 2.34
3886  */
3887 const char *
3888 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo *info)
3889 {
3890   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
3891
3892   return info->startup_wm_class;
3893 }
3894
3895 /**
3896  * g_desktop_app_info_get_string:
3897  * @info: a #GDesktopAppInfo
3898  * @key: the key to look up
3899  *
3900  * Looks up a string value in the keyfile backing @info.
3901  *
3902  * The @key is looked up in the "Desktop Entry" group.
3903  *
3904  * Returns: a newly allocated string, or %NULL if the key
3905  *     is not found
3906  *
3907  * Since: 2.36
3908  */
3909 char *
3910 g_desktop_app_info_get_string (GDesktopAppInfo *info,
3911                                const char      *key)
3912 {
3913   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
3914
3915   return g_key_file_get_string (info->keyfile,
3916                                 G_KEY_FILE_DESKTOP_GROUP, key, NULL);
3917 }
3918
3919 /**
3920  * g_desktop_app_info_get_boolean:
3921  * @info: a #GDesktopAppInfo
3922  * @key: the key to look up
3923  *
3924  * Looks up a boolean value in the keyfile backing @info.
3925  *
3926  * The @key is looked up in the "Desktop Entry" group.
3927  *
3928  * Returns: the boolean value, or %FALSE if the key
3929  *     is not found
3930  *
3931  * Since: 2.36
3932  */
3933 gboolean
3934 g_desktop_app_info_get_boolean (GDesktopAppInfo *info,
3935                                 const char      *key)
3936 {
3937   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
3938
3939   return g_key_file_get_boolean (info->keyfile,
3940                                  G_KEY_FILE_DESKTOP_GROUP, key, NULL);
3941 }
3942
3943 /**
3944  * g_desktop_app_info_has_key:
3945  * @info: a #GDesktopAppInfo
3946  * @key: the key to look up
3947  *
3948  * Returns whether @key exists in the "Desktop Entry" group
3949  * of the keyfile backing @info.
3950  *
3951  * Returns: %TRUE if the @key exists
3952  *
3953  * Since: 2.26
3954  */
3955 gboolean
3956 g_desktop_app_info_has_key (GDesktopAppInfo *info,
3957                             const char      *key)
3958 {
3959   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
3960
3961   return g_key_file_has_key (info->keyfile,
3962                              G_KEY_FILE_DESKTOP_GROUP, key, NULL);
3963 }
3964
3965 /* Desktop actions support {{{2 */
3966
3967 /**
3968  * g_desktop_app_info_list_actions:
3969  * @info: a #GDesktopAppInfo
3970  *
3971  * Returns the list of "additional application actions" supported on the
3972  * desktop file, as per the desktop file specification.
3973  *
3974  * As per the specification, this is the list of actions that are
3975  * explicitly listed in the "Actions" key of the [Desktop Entry] group.
3976  *
3977  * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
3978  *
3979  * Since: 2.38
3980  **/
3981 const gchar * const *
3982 g_desktop_app_info_list_actions (GDesktopAppInfo *info)
3983 {
3984   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
3985
3986   return (const gchar **) info->actions;
3987 }
3988
3989 static gboolean
3990 app_info_has_action (GDesktopAppInfo *info,
3991                      const gchar     *action_name)
3992 {
3993   gint i;
3994
3995   for (i = 0; info->actions[i]; i++)
3996     if (g_str_equal (info->actions[i], action_name))
3997       return TRUE;
3998
3999   return FALSE;
4000 }
4001
4002 /**
4003  * g_desktop_app_info_get_action_name:
4004  * @info: a #GDesktopAppInfo
4005  * @action_name: the name of the action as from
4006  *   g_desktop_app_info_list_actions()
4007  *
4008  * Gets the user-visible display name of the "additional application
4009  * action" specified by @action_name.
4010  *
4011  * This corresponds to the "Name" key within the keyfile group for the
4012  * action.
4013  *
4014  * Returns: (transfer full): the locale-specific action name
4015  *
4016  * Since: 2.38
4017  */
4018 gchar *
4019 g_desktop_app_info_get_action_name (GDesktopAppInfo *info,
4020                                     const gchar     *action_name)
4021 {
4022   gchar *group_name;
4023   gchar *result;
4024
4025   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4026   g_return_val_if_fail (action_name != NULL, NULL);
4027   g_return_val_if_fail (app_info_has_action (info, action_name), NULL);
4028
4029   group_name = g_strdup_printf ("Desktop Action %s", action_name);
4030   result = g_key_file_get_locale_string (info->keyfile, group_name, "Name", NULL, NULL);
4031   g_free (group_name);
4032
4033   /* The spec says that the Name field must be given.
4034    *
4035    * If it's not, let's follow the behaviour of our get_name()
4036    * implementation above and never return %NULL.
4037    */
4038   if (result == NULL)
4039     result = g_strdup (_("Unnamed"));
4040
4041   return result;
4042 }
4043
4044 /**
4045  * g_desktop_app_info_launch_action:
4046  * @info: a #GDesktopAppInfo
4047  * @action_name: the name of the action as from
4048  *   g_desktop_app_info_list_actions()
4049  * @launch_context: (allow-none): a #GAppLaunchContext
4050  *
4051  * Activates the named application action.
4052  *
4053  * You may only call this function on action names that were
4054  * returned from g_desktop_app_info_list_actions().
4055  *
4056  * Note that if the main entry of the desktop file indicates that the
4057  * application supports startup notification, and @launch_context is
4058  * non-%NULL, then startup notification will be used when activating the
4059  * action (and as such, invocation of the action on the receiving side
4060  * must signal the end of startup notification when it is completed).
4061  * This is the expected behaviour of applications declaring additional
4062  * actions, as per the desktop file specification.
4063  *
4064  * As with g_app_info_launch() there is no way to detect failures that
4065  * occur while using this function.
4066  *
4067  * Since: 2.38
4068  */
4069 void
4070 g_desktop_app_info_launch_action (GDesktopAppInfo   *info,
4071                                   const gchar       *action_name,
4072                                   GAppLaunchContext *launch_context)
4073 {
4074   GDBusConnection *session_bus;
4075
4076   g_return_if_fail (G_IS_DESKTOP_APP_INFO (info));
4077   g_return_if_fail (action_name != NULL);
4078   g_return_if_fail (app_info_has_action (info, action_name));
4079
4080   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
4081
4082   if (session_bus && info->app_id)
4083     {
4084       gchar *object_path;
4085
4086       object_path = object_path_from_appid (info->app_id);
4087       g_dbus_connection_call (session_bus, info->app_id, object_path,
4088                               "org.freedesktop.Application", "ActivateAction",
4089                               g_variant_new ("(sav@a{sv})", action_name, NULL,
4090                                              g_desktop_app_info_make_platform_data (info, NULL, launch_context)),
4091                               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
4092       g_free (object_path);
4093     }
4094   else
4095     {
4096       gchar *group_name;
4097       gchar *exec_line;
4098
4099       group_name = g_strdup_printf ("Desktop Action %s", action_name);
4100       exec_line = g_key_file_get_string (info->keyfile, group_name, "Exec", NULL);
4101       g_free (group_name);
4102
4103       if (exec_line)
4104         g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
4105                                                    _SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL, NULL);
4106     }
4107
4108   if (session_bus != NULL)
4109     {
4110       g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
4111       g_object_unref (session_bus);
4112     }
4113 }
4114 /* Epilogue {{{1 */
4115
4116 /* vim:set foldmethod=marker: */