Imported Upstream version 2.62.3
[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.1 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, see <http://www.gnu.org/licenses/>.
18  *
19  * Author: Alexander Larsson <alexl@redhat.com>
20  *         Ryan Lortie <desrt@desrt.ca>
21  */
22
23 /* Prelude {{{1 */
24
25 #include "config.h"
26
27 /* For the #GDesktopAppInfoLookup macros; since macro deprecation is implemented
28  * in the preprocessor, we need to define this before including glib.h*/
29 #define GLIB_DISABLE_DEPRECATION_WARNINGS
30
31 #include <errno.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #ifdef HAVE_CRT_EXTERNS_H
36 #include <crt_externs.h>
37 #endif
38
39 #include "gcontenttypeprivate.h"
40 #include "gdesktopappinfo.h"
41 #ifdef G_OS_UNIX
42 #include "glib-unix.h"
43 #endif
44 #include "gfile.h"
45 #include "gioerror.h"
46 #include "gthemedicon.h"
47 #include "gfileicon.h"
48 #include <glib/gstdio.h>
49 #include "glibintl.h"
50 #include "giomodule-priv.h"
51 #include "gappinfo.h"
52 #include "gappinfoprivate.h"
53 #include "glocalfilemonitor.h"
54
55 #ifdef G_OS_UNIX
56 #include "gdocumentportal.h"
57 #endif
58
59 /**
60  * SECTION:gdesktopappinfo
61  * @title: GDesktopAppInfo
62  * @short_description: Application information from desktop files
63  * @include: gio/gdesktopappinfo.h
64  *
65  * #GDesktopAppInfo is an implementation of #GAppInfo based on
66  * desktop files.
67  *
68  * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
69  * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
70  * file when using it.
71  */
72
73 #define DEFAULT_APPLICATIONS_GROUP  "Default Applications"
74 #define ADDED_ASSOCIATIONS_GROUP    "Added Associations"
75 #define REMOVED_ASSOCIATIONS_GROUP  "Removed Associations"
76 #define MIME_CACHE_GROUP            "MIME Cache"
77 #define GENERIC_NAME_KEY            "GenericName"
78 #define FULL_NAME_KEY               "X-GNOME-FullName"
79 #define KEYWORDS_KEY                "Keywords"
80 #define STARTUP_WM_CLASS_KEY        "StartupWMClass"
81
82 enum {
83   PROP_0,
84   PROP_FILENAME
85 };
86
87 static void     g_desktop_app_info_iface_init         (GAppInfoIface    *iface);
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 /* DesktopFileDir implementation {{{1 */
143
144 typedef struct
145 {
146   gatomicrefcount             ref_count;
147   gchar                      *path;
148   gchar                      *alternatively_watching;
149   gboolean                    is_config;
150   gboolean                    is_setup;
151   GFileMonitor               *monitor;
152   GHashTable                 *app_names;
153   GHashTable                 *mime_tweaks;
154   GHashTable                 *memory_index;
155   GHashTable                 *memory_implementations;
156 } DesktopFileDir;
157
158 static GPtrArray      *desktop_file_dirs = NULL;
159 static const gchar    *desktop_file_dirs_config_dir = NULL;
160 static DesktopFileDir *desktop_file_dir_user_config = NULL;  /* (owned) */
161 static DesktopFileDir *desktop_file_dir_user_data = NULL;  /* (owned) */
162 static GMutex          desktop_file_dir_lock;
163 static const gchar    *gio_launch_desktop_path = NULL;
164
165 /* Monitor 'changed' signal handler {{{2 */
166 static void desktop_file_dir_reset (DesktopFileDir *dir);
167
168 static DesktopFileDir *
169 desktop_file_dir_ref (DesktopFileDir *dir)
170 {
171   g_atomic_ref_count_inc (&dir->ref_count);
172
173   return dir;
174 }
175
176 static void
177 desktop_file_dir_unref (DesktopFileDir *dir)
178 {
179   if (g_atomic_ref_count_dec (&dir->ref_count))
180     {
181       desktop_file_dir_reset (dir);
182       g_free (dir->path);
183       g_free (dir);
184     }
185 }
186
187 /*< internal >
188  * desktop_file_dir_get_alternative_dir:
189  * @dir: a #DesktopFileDir
190  *
191  * Gets the "alternative" directory to monitor in case the path
192  * doesn't exist.
193  *
194  * If the path exists this will return NULL, otherwise it will return a
195  * parent directory of the path.
196  *
197  * This is used to avoid inotify on a non-existent directory (which
198  * results in polling).
199  *
200  * See https://bugzilla.gnome.org/show_bug.cgi?id=522314 for more info.
201  */
202 static gchar *
203 desktop_file_dir_get_alternative_dir (DesktopFileDir *dir)
204 {
205   gchar *parent;
206
207   /* If the directory itself exists then we need no alternative. */
208   if (g_access (dir->path, R_OK | X_OK) == 0)
209     return NULL;
210
211   /* Otherwise, try the parent directories until we find one. */
212   parent = g_path_get_dirname (dir->path);
213
214   while (g_access (parent, R_OK | X_OK) != 0)
215     {
216       gchar *tmp = parent;
217
218       parent = g_path_get_dirname (tmp);
219
220       /* If somehow we get to '/' or '.' then just stop... */
221       if (g_str_equal (parent, tmp))
222         {
223           g_free (tmp);
224           break;
225         }
226
227       g_free (tmp);
228     }
229
230   return parent;
231 }
232
233 static void
234 desktop_file_dir_changed (GFileMonitor      *monitor,
235                           GFile             *file,
236                           GFile             *other_file,
237                           GFileMonitorEvent  event_type,
238                           gpointer           user_data)
239 {
240   DesktopFileDir *dir = user_data;
241   gboolean do_nothing = FALSE;
242
243   /* We are not interested in receiving notifications forever just
244    * because someone asked about one desktop file once.
245    *
246    * After we receive the first notification, reset the dir, destroying
247    * the monitor.  We will take this as a hint, next time that we are
248    * asked, that we need to check if everything is up to date.
249    *
250    * If this is a notification for a parent directory (because the
251    * desktop directory didn't exist) then we shouldn't fire the signal
252    * unless something actually changed.
253    */
254   g_mutex_lock (&desktop_file_dir_lock);
255
256   if (dir->alternatively_watching)
257     {
258       gchar *alternative_dir;
259
260       alternative_dir = desktop_file_dir_get_alternative_dir (dir);
261       do_nothing = alternative_dir && g_str_equal (dir->alternatively_watching, alternative_dir);
262       g_free (alternative_dir);
263     }
264
265   if (!do_nothing)
266     desktop_file_dir_reset (dir);
267
268   g_mutex_unlock (&desktop_file_dir_lock);
269
270   /* Notify anyone else who may be interested */
271   if (!do_nothing)
272     g_app_info_monitor_fire ();
273 }
274
275 /* Internal utility functions {{{2 */
276
277 /*< internal >
278  * desktop_file_dir_app_name_is_masked:
279  * @dir: a #DesktopFileDir
280  * @app_name: an application ID
281  *
282  * Checks if @app_name is masked for @dir.
283  *
284  * An application is masked if a similarly-named desktop file exists in
285  * a desktop file directory with higher precedence.  Masked desktop
286  * files should be ignored.
287  */
288 static gboolean
289 desktop_file_dir_app_name_is_masked (DesktopFileDir *dir,
290                                      const gchar    *app_name)
291 {
292   guint i;
293
294   for (i = 0; i < desktop_file_dirs->len; i++)
295     {
296       DesktopFileDir *i_dir = g_ptr_array_index (desktop_file_dirs, i);
297
298       if (dir == i_dir)
299         return FALSE;
300       if (i_dir->app_names && g_hash_table_contains (i_dir->app_names, app_name))
301         return TRUE;
302     }
303
304   return FALSE;
305 }
306
307 static const gchar * const *
308 get_lowercase_current_desktops (void)
309 {
310   static gchar **result;
311
312   if (g_once_init_enter (&result))
313     {
314       const gchar *envvar;
315       gchar **tmp;
316
317       envvar = g_getenv ("XDG_CURRENT_DESKTOP");
318
319       if (envvar)
320         {
321           gint i, j;
322
323           tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
324
325           for (i = 0; tmp[i]; i++)
326             for (j = 0; tmp[i][j]; j++)
327               tmp[i][j] = g_ascii_tolower (tmp[i][j]);
328         }
329       else
330         tmp = g_new0 (gchar *, 0 + 1);
331
332       g_once_init_leave (&result, tmp);
333     }
334
335   return (const gchar **) result;
336 }
337
338 static const gchar * const *
339 get_current_desktops (const gchar *value)
340 {
341   static gchar **result;
342
343   if (g_once_init_enter (&result))
344     {
345       gchar **tmp;
346
347       if (!value)
348         value = g_getenv ("XDG_CURRENT_DESKTOP");
349
350       if (!value)
351         value = "";
352
353       tmp = g_strsplit (value, ":", 0);
354
355       g_once_init_leave (&result, tmp);
356     }
357
358   return (const gchar **) result;
359 }
360
361 /*< internal >
362  * add_to_table_if_appropriate:
363  * @apps: a string to GDesktopAppInfo hash table
364  * @app_name: the name of the application
365  * @info: a #GDesktopAppInfo, or NULL
366  *
367  * If @info is non-%NULL and non-hidden, then add it to @apps, using
368  * @app_name as a key.
369  *
370  * If @info is non-%NULL then this function will consume the passed-in
371  * reference.
372  */
373 static void
374 add_to_table_if_appropriate (GHashTable      *apps,
375                              const gchar     *app_name,
376                              GDesktopAppInfo *info)
377 {
378   if (!info)
379     return;
380
381   if (info->hidden)
382     {
383       g_object_unref (info);
384       return;
385     }
386
387   g_free (info->desktop_id);
388   info->desktop_id = g_strdup (app_name);
389
390   g_hash_table_insert (apps, g_strdup (info->desktop_id), info);
391 }
392
393 enum
394 {
395   DESKTOP_KEY_Comment,
396   DESKTOP_KEY_Exec,
397   DESKTOP_KEY_GenericName,
398   DESKTOP_KEY_Keywords,
399   DESKTOP_KEY_Name,
400   DESKTOP_KEY_X_GNOME_FullName,
401
402   N_DESKTOP_KEYS
403 };
404
405 const gchar desktop_key_match_category[N_DESKTOP_KEYS] = {
406   /* Note: lower numbers are a better match.
407    *
408    * In case we want two keys to match at the same level, we can just
409    * use the same number for the two different keys.
410    */
411   [DESKTOP_KEY_Name]             = 1,
412   [DESKTOP_KEY_Exec]             = 2,
413   [DESKTOP_KEY_Keywords]         = 3,
414   [DESKTOP_KEY_GenericName]      = 4,
415   [DESKTOP_KEY_X_GNOME_FullName] = 5,
416   [DESKTOP_KEY_Comment]          = 6
417 };
418
419 /* Common prefix commands to ignore from Exec= lines */
420 const char * const exec_key_match_blacklist[] = {
421   "bash",
422   "env",
423   "flatpak",
424   "gjs",
425   "pkexec",
426   "python",
427   "python2",
428   "python3",
429   "sh",
430   "wine",
431   "wine64",
432   NULL
433 };
434
435 static gchar *
436 desktop_key_get_name (guint key_id)
437 {
438   switch (key_id)
439     {
440     case DESKTOP_KEY_Comment:
441       return "Comment";
442     case DESKTOP_KEY_Exec:
443       return "Exec";
444     case DESKTOP_KEY_GenericName:
445       return GENERIC_NAME_KEY;
446     case DESKTOP_KEY_Keywords:
447       return KEYWORDS_KEY;
448     case DESKTOP_KEY_Name:
449       return "Name";
450     case DESKTOP_KEY_X_GNOME_FullName:
451       return FULL_NAME_KEY;
452     default:
453       g_assert_not_reached ();
454     }
455 }
456
457 /* Search global state {{{2
458  *
459  * We only ever search under a global lock, so we can use (and reuse)
460  * some global data to reduce allocations made while searching.
461  *
462  * In short, we keep around arrays of results that we expand as needed
463  * (and never shrink).
464  *
465  * static_token_results: this is where we append the results for each
466  *     token within a given desktop directory, as we handle it (which is
467  *     a union of all matches for this term)
468  *
469  * static_search_results: this is where we build the complete results
470  *     for a single directory (which is an intersection of the matches
471  *     found for each term)
472  *
473  * static_total_results: this is where we build the complete results
474  *     across all directories (which is a union of the matches found in
475  *     each directory)
476  *
477  * The app_names that enter these tables are always pointer-unique (in
478  * the sense that string equality is the same as pointer equality).
479  * This can be guaranteed for two reasons:
480  *
481  *   - we mask appids so that a given appid will only ever appear within
482  *     the highest-precedence directory that contains it.  We never
483  *     return search results from a lower-level directory if a desktop
484  *     file exists in a higher-level one.
485  *
486  *   - within a given directory, the string is unique because it's the
487  *     key in the hashtable of all app_ids for that directory.
488  *
489  * We perform a merging of the results in merge_token_results().  This
490  * works by ordering the two lists and moving through each of them (at
491  * the same time) looking for common elements, rejecting uncommon ones.
492  * "Order" here need not mean any particular thing, as long as it is
493  * some order.  Because of the uniqueness of our strings, we can use
494  * pointer order.  That's what's going on in compare_results() below.
495  */
496 struct search_result
497 {
498   const gchar *app_name;
499   gint         category;
500 };
501
502 static struct search_result *static_token_results;
503 static gint                  static_token_results_size;
504 static gint                  static_token_results_allocated;
505 static struct search_result *static_search_results;
506 static gint                  static_search_results_size;
507 static gint                  static_search_results_allocated;
508 static struct search_result *static_total_results;
509 static gint                  static_total_results_size;
510 static gint                  static_total_results_allocated;
511
512 /* And some functions for performing nice operations against it */
513 static gint
514 compare_results (gconstpointer a,
515                  gconstpointer b)
516 {
517   const struct search_result *ra = a;
518   const struct search_result *rb = b;
519
520   if (ra->app_name < rb->app_name)
521     return -1;
522
523   else if (ra->app_name > rb->app_name)
524     return 1;
525
526   else
527     return ra->category - rb->category;
528 }
529
530 static gint
531 compare_categories (gconstpointer a,
532                     gconstpointer b)
533 {
534   const struct search_result *ra = a;
535   const struct search_result *rb = b;
536
537   return ra->category - rb->category;
538 }
539
540 static void
541 add_token_result (const gchar *app_name,
542                   guint16      category)
543 {
544   if G_UNLIKELY (static_token_results_size == static_token_results_allocated)
545     {
546       static_token_results_allocated = MAX (16, static_token_results_allocated * 2);
547       static_token_results = g_renew (struct search_result, static_token_results, static_token_results_allocated);
548     }
549
550   static_token_results[static_token_results_size].app_name = app_name;
551   static_token_results[static_token_results_size].category = category;
552   static_token_results_size++;
553 }
554
555 static void
556 merge_token_results (gboolean first)
557 {
558   if (static_token_results_size != 0)
559     qsort (static_token_results, static_token_results_size, sizeof (struct search_result), compare_results);
560
561   /* If this is the first token then we are basically merging a list with
562    * itself -- we only perform de-duplication.
563    *
564    * If this is not the first token then we are doing a real merge.
565    */
566   if (first)
567     {
568       const gchar *last_name = NULL;
569       gint i;
570
571       /* We must de-duplicate, but we do so by taking the best category
572        * in each case.
573        *
574        * The final list can be as large as the input here, so make sure
575        * we have enough room (even if it's too much room).
576        */
577
578       if G_UNLIKELY (static_search_results_allocated < static_token_results_size)
579         {
580           static_search_results_allocated = static_token_results_allocated;
581           static_search_results = g_renew (struct search_result,
582                                            static_search_results,
583                                            static_search_results_allocated);
584         }
585
586       for (i = 0; i < static_token_results_size; i++)
587         {
588           /* The list is sorted so that the best match for a given id
589            * will be at the front, so once we have copied an id, skip
590            * the rest of the entries for the same id.
591            */
592           if (static_token_results[i].app_name == last_name)
593             continue;
594
595           last_name = static_token_results[i].app_name;
596
597           static_search_results[static_search_results_size++] = static_token_results[i];
598         }
599     }
600   else
601     {
602       const gchar *last_name = NULL;
603       gint i, j = 0;
604       gint k = 0;
605
606       /* We only ever remove items from the results list, so no need to
607        * resize to ensure that we have enough room.
608        */
609       for (i = 0; i < static_token_results_size; i++)
610         {
611           if (static_token_results[i].app_name == last_name)
612             continue;
613
614           last_name = static_token_results[i].app_name;
615
616           /* Now we only want to have a result in static_search_results
617            * if we already have it there *and* we have it in
618            * static_token_results as well.  The category will be the
619            * lesser of the two.
620            *
621            * Skip past the results in static_search_results that are not
622            * going to be matches.
623            */
624           while (k < static_search_results_size &&
625                  static_search_results[k].app_name < static_token_results[i].app_name)
626             k++;
627
628           if (k < static_search_results_size &&
629               static_search_results[k].app_name == static_token_results[i].app_name)
630             {
631               /* We have a match.
632                *
633                * Category should be the worse of the two (ie:
634                * numerically larger).
635                */
636               static_search_results[j].app_name = static_search_results[k].app_name;
637               static_search_results[j].category = MAX (static_search_results[k].category,
638                                                        static_token_results[i].category);
639               j++;
640             }
641         }
642
643       static_search_results_size = j;
644     }
645
646   /* Clear it out for next time... */
647   static_token_results_size = 0;
648 }
649
650 static void
651 reset_total_search_results (void)
652 {
653   static_total_results_size = 0;
654 }
655
656 static void
657 sort_total_search_results (void)
658 {
659   if (static_total_results_size != 0)
660     qsort (static_total_results, static_total_results_size, sizeof (struct search_result), compare_categories);
661 }
662
663 static void
664 merge_directory_results (void)
665 {
666   if G_UNLIKELY (static_total_results_size + static_search_results_size > static_total_results_allocated)
667     {
668       static_total_results_allocated = MAX (16, static_total_results_allocated);
669       while (static_total_results_allocated < static_total_results_size + static_search_results_size)
670         static_total_results_allocated *= 2;
671       static_total_results = g_renew (struct search_result, static_total_results, static_total_results_allocated);
672     }
673
674   if (static_total_results + static_total_results_size != 0)
675     memcpy (static_total_results + static_total_results_size,
676             static_search_results,
677             static_search_results_size * sizeof (struct search_result));
678
679   static_total_results_size += static_search_results_size;
680
681   /* Clear it out for next time... */
682   static_search_results_size = 0;
683 }
684
685 /* Support for unindexed DesktopFileDirs {{{2 */
686 static void
687 get_apps_from_dir (GHashTable **apps,
688                    const char  *dirname,
689                    const char  *prefix)
690 {
691   const char *basename;
692   GDir *dir;
693
694   dir = g_dir_open (dirname, 0, NULL);
695
696   if (dir == NULL)
697     return;
698
699   while ((basename = g_dir_read_name (dir)) != NULL)
700     {
701       gchar *filename;
702
703       filename = g_build_filename (dirname, basename, NULL);
704
705       if (g_str_has_suffix (basename, ".desktop"))
706         {
707           gchar *app_name;
708
709           app_name = g_strconcat (prefix, basename, NULL);
710
711           if (*apps == NULL)
712             *apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
713
714           g_hash_table_insert (*apps, app_name, g_strdup (filename));
715         }
716       else if (g_file_test (filename, G_FILE_TEST_IS_DIR))
717         {
718           gchar *subprefix;
719
720           subprefix = g_strconcat (prefix, basename, "-", NULL);
721           get_apps_from_dir (apps, filename, subprefix);
722           g_free (subprefix);
723         }
724
725       g_free (filename);
726     }
727
728   g_dir_close (dir);
729 }
730
731 typedef struct
732 {
733   gchar **additions;
734   gchar **removals;
735   gchar **defaults;
736 } UnindexedMimeTweaks;
737
738 static void
739 free_mime_tweaks (gpointer data)
740 {
741   UnindexedMimeTweaks *tweaks = data;
742
743   g_strfreev (tweaks->additions);
744   g_strfreev (tweaks->removals);
745   g_strfreev (tweaks->defaults);
746
747   g_slice_free (UnindexedMimeTweaks, tweaks);
748 }
749
750 static UnindexedMimeTweaks *
751 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir *dir,
752                                        const gchar    *mime_type)
753 {
754   UnindexedMimeTweaks *tweaks;
755   gchar *unaliased_type;
756
757   unaliased_type = _g_unix_content_type_unalias (mime_type);
758   tweaks = g_hash_table_lookup (dir->mime_tweaks, unaliased_type);
759
760   if (tweaks == NULL)
761     {
762       tweaks = g_slice_new0 (UnindexedMimeTweaks);
763       g_hash_table_insert (dir->mime_tweaks, unaliased_type, tweaks);
764     }
765   else
766     g_free (unaliased_type);
767
768   return tweaks;
769 }
770
771 /* consumes 'to_add' */
772 static void
773 expand_strv (gchar         ***strv_ptr,
774              gchar          **to_add,
775              gchar * const   *blacklist)
776 {
777   guint strv_len, add_len;
778   gchar **strv;
779   guint i, j;
780
781   if (!*strv_ptr)
782     {
783       *strv_ptr = to_add;
784       return;
785     }
786
787   strv = *strv_ptr;
788   strv_len = g_strv_length (strv);
789   add_len = g_strv_length (to_add);
790   strv = g_renew (gchar *, strv, strv_len + add_len + 1);
791
792   for (i = 0; to_add[i]; i++)
793     {
794       /* Don't add blacklisted strings */
795       if (blacklist)
796         for (j = 0; blacklist[j]; j++)
797           if (g_str_equal (to_add[i], blacklist[j]))
798             goto no_add;
799
800       /* Don't add duplicates already in the list */
801       for (j = 0; j < strv_len; j++)
802         if (g_str_equal (to_add[i], strv[j]))
803           goto no_add;
804
805       strv[strv_len++] = to_add[i];
806       continue;
807
808 no_add:
809       g_free (to_add[i]);
810     }
811
812   strv[strv_len] = NULL;
813   *strv_ptr = strv;
814
815   g_free (to_add);
816 }
817
818 static void
819 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir,
820                                                const gchar    *filename,
821                                                const gchar    *added_group,
822                                                gboolean        tweaks_permitted)
823 {
824   UnindexedMimeTweaks *tweaks;
825   char **desktop_file_ids;
826   GKeyFile *key_file;
827   gchar **mime_types;
828   int i;
829
830   key_file = g_key_file_new ();
831   if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL))
832     {
833       g_key_file_free (key_file);
834       return;
835     }
836
837   mime_types = g_key_file_get_keys (key_file, added_group, NULL, NULL);
838
839   if G_UNLIKELY (mime_types != NULL && !tweaks_permitted)
840     {
841       g_warning ("%s contains a [%s] group, but it is not permitted here.  Only the non-desktop-specific "
842                  "mimeapps.list file may add or remove associations.", filename, added_group);
843       g_strfreev (mime_types);
844       mime_types = NULL;
845     }
846
847   if (mime_types != NULL)
848     {
849       for (i = 0; mime_types[i] != NULL; i++)
850         {
851           desktop_file_ids = g_key_file_get_string_list (key_file, added_group, mime_types[i], NULL, NULL);
852
853           if (desktop_file_ids)
854             {
855               tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
856               expand_strv (&tweaks->additions, desktop_file_ids, tweaks->removals);
857             }
858         }
859
860       g_strfreev (mime_types);
861     }
862
863   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
864
865   if G_UNLIKELY (mime_types != NULL && !tweaks_permitted)
866     {
867       g_warning ("%s contains a [%s] group, but it is not permitted here.  Only the non-desktop-specific "
868                  "mimeapps.list file may add or remove associations.", filename, REMOVED_ASSOCIATIONS_GROUP);
869       g_strfreev (mime_types);
870       mime_types = NULL;
871     }
872
873   if (mime_types != NULL)
874     {
875       for (i = 0; mime_types[i] != NULL; i++)
876         {
877           desktop_file_ids = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP, mime_types[i], NULL, NULL);
878
879           if (desktop_file_ids)
880             {
881               tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
882               expand_strv (&tweaks->removals, desktop_file_ids, tweaks->additions);
883             }
884         }
885
886       g_strfreev (mime_types);
887     }
888
889   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, NULL, NULL);
890
891   if (mime_types != NULL)
892     {
893       for (i = 0; mime_types[i] != NULL; i++)
894         {
895           desktop_file_ids = g_key_file_get_string_list (key_file, DEFAULT_APPLICATIONS_GROUP, mime_types[i], NULL, NULL);
896
897           if (desktop_file_ids)
898             {
899               tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
900               expand_strv (&tweaks->defaults, desktop_file_ids, NULL);
901             }
902         }
903
904       g_strfreev (mime_types);
905     }
906
907   g_key_file_free (key_file);
908 }
909
910 static void
911 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir *dir)
912 {
913   const gchar * const *desktops;
914   gchar *filename;
915   gint i;
916
917   dir->mime_tweaks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_mime_tweaks);
918
919   /* We process in order of precedence, using a blacklisting approach to
920    * avoid recording later instructions that conflict with ones we found
921    * earlier.
922    *
923    * We first start with the XDG_CURRENT_DESKTOP files, in precedence
924    * order.
925    */
926   desktops = get_lowercase_current_desktops ();
927   for (i = 0; desktops[i]; i++)
928     {
929       filename = g_strdup_printf ("%s/%s-mimeapps.list", dir->path, desktops[i]);
930       desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, FALSE);
931       g_free (filename);
932     }
933
934   /* Next, the non-desktop-specific mimeapps.list */
935   filename = g_strdup_printf ("%s/mimeapps.list", dir->path);
936   desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, TRUE);
937   g_free (filename);
938
939   /* The remaining files are only checked for in directories that might
940    * contain desktop files (ie: not the config dirs).
941    */
942   if (dir->is_config)
943     return;
944
945   /* We have 'defaults.list' which was only ever understood by GLib.  It
946    * exists widely, but it has never been part of any spec and it should
947    * be treated as deprecated.  This will be removed in a future
948    * version.
949    */
950   filename = g_strdup_printf ("%s/defaults.list", dir->path);
951   desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, FALSE);
952   g_free (filename);
953
954   /* Finally, the mimeinfo.cache, which is just a cached copy of what we
955    * would find in the MimeTypes= lines of all of the desktop files.
956    */
957   filename = g_strdup_printf ("%s/mimeinfo.cache", dir->path);
958   desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, MIME_CACHE_GROUP, TRUE);
959   g_free (filename);
960 }
961
962 static void
963 desktop_file_dir_unindexed_init (DesktopFileDir *dir)
964 {
965   if (!dir->is_config)
966     get_apps_from_dir (&dir->app_names, dir->path, "");
967
968   desktop_file_dir_unindexed_read_mimeapps_lists (dir);
969 }
970
971 static GDesktopAppInfo *
972 desktop_file_dir_unindexed_get_app (DesktopFileDir *dir,
973                                     const gchar    *desktop_id)
974 {
975   const gchar *filename;
976
977   filename = g_hash_table_lookup (dir->app_names, desktop_id);
978
979   if (!filename)
980     return NULL;
981
982   return g_desktop_app_info_new_from_filename (filename);
983 }
984
985 static void
986 desktop_file_dir_unindexed_get_all (DesktopFileDir *dir,
987                                     GHashTable     *apps)
988 {
989   GHashTableIter iter;
990   gpointer app_name;
991   gpointer filename;
992
993   if (dir->app_names == NULL)
994     return;
995
996   g_hash_table_iter_init (&iter, dir->app_names);
997   while (g_hash_table_iter_next (&iter, &app_name, &filename))
998     {
999       if (desktop_file_dir_app_name_is_masked (dir, app_name))
1000         continue;
1001
1002       add_to_table_if_appropriate (apps, app_name, g_desktop_app_info_new_from_filename (filename));
1003     }
1004 }
1005
1006 typedef struct _MemoryIndexEntry MemoryIndexEntry;
1007 typedef GHashTable MemoryIndex;
1008
1009 struct _MemoryIndexEntry
1010 {
1011   const gchar      *app_name; /* pointer to the hashtable key */
1012   gint              match_category;
1013   MemoryIndexEntry *next;
1014 };
1015
1016 static void
1017 memory_index_entry_free (gpointer data)
1018 {
1019   MemoryIndexEntry *mie = data;
1020
1021   while (mie)
1022     {
1023       MemoryIndexEntry *next = mie->next;
1024
1025       g_slice_free (MemoryIndexEntry, mie);
1026       mie = next;
1027     }
1028 }
1029
1030 static void
1031 memory_index_add_token (MemoryIndex *mi,
1032                         const gchar *token,
1033                         gint         match_category,
1034                         const gchar *app_name)
1035 {
1036   MemoryIndexEntry *mie, *first;
1037
1038   mie = g_slice_new (MemoryIndexEntry);
1039   mie->app_name = app_name;
1040   mie->match_category = match_category;
1041
1042   first = g_hash_table_lookup (mi, token);
1043
1044   if (first)
1045     {
1046       mie->next = first->next;
1047       first->next = mie;
1048     }
1049   else
1050     {
1051       mie->next = NULL;
1052       g_hash_table_insert (mi, g_strdup (token), mie);
1053     }
1054 }
1055
1056 static void
1057 memory_index_add_string (MemoryIndex *mi,
1058                          const gchar *string,
1059                          gint         match_category,
1060                          const gchar *app_name)
1061 {
1062   gchar **tokens, **alternates;
1063   gint i;
1064
1065   tokens = g_str_tokenize_and_fold (string, NULL, &alternates);
1066
1067   for (i = 0; tokens[i]; i++)
1068     memory_index_add_token (mi, tokens[i], match_category, app_name);
1069
1070   for (i = 0; alternates[i]; i++)
1071     memory_index_add_token (mi, alternates[i], match_category, app_name);
1072
1073   g_strfreev (alternates);
1074   g_strfreev (tokens);
1075 }
1076
1077 static MemoryIndex *
1078 memory_index_new (void)
1079 {
1080   return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, memory_index_entry_free);
1081 }
1082
1083 static void
1084 desktop_file_dir_unindexed_setup_search (DesktopFileDir *dir)
1085 {
1086   GHashTableIter iter;
1087   gpointer app, path;
1088
1089   dir->memory_index = memory_index_new ();
1090   dir->memory_implementations = memory_index_new ();
1091
1092   /* Nothing to search? */
1093   if (dir->app_names == NULL)
1094     return;
1095
1096   g_hash_table_iter_init (&iter, dir->app_names);
1097   while (g_hash_table_iter_next (&iter, &app, &path))
1098     {
1099       GKeyFile *key_file;
1100
1101       if (desktop_file_dir_app_name_is_masked (dir, app))
1102         continue;
1103
1104       key_file = g_key_file_new ();
1105
1106       if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL) &&
1107           !g_key_file_get_boolean (key_file, "Desktop Entry", "Hidden", NULL))
1108         {
1109           /* Index the interesting keys... */
1110           gchar **implements;
1111           gint i;
1112
1113           for (i = 0; i < G_N_ELEMENTS (desktop_key_match_category); i++)
1114             {
1115               const gchar *value;
1116               gchar *raw;
1117
1118               if (!desktop_key_match_category[i])
1119                 continue;
1120
1121               raw = g_key_file_get_locale_string (key_file, "Desktop Entry", desktop_key_get_name (i), NULL, NULL);
1122               value = raw;
1123
1124               if (i == DESKTOP_KEY_Exec && raw != NULL)
1125                 {
1126                   /* Special handling: only match basename of first field */
1127                   gchar *space;
1128                   gchar *slash;
1129
1130                   /* Remove extra arguments, if any */
1131                   space = raw + strcspn (raw, " \t\n"); /* IFS */
1132                   *space = '\0';
1133
1134                   /* Skip the pathname, if any */
1135                   if ((slash = strrchr (raw, '/')))
1136                     value = slash + 1;
1137
1138                   /* Don't match on blacklisted binaries like interpreters */
1139                   if (g_strv_contains (exec_key_match_blacklist, value))
1140                     value = NULL;
1141                 }
1142
1143               if (value)
1144                 memory_index_add_string (dir->memory_index, value, desktop_key_match_category[i], app);
1145
1146               g_free (raw);
1147             }
1148
1149           /* Make note of the Implements= line */
1150           implements = g_key_file_get_string_list (key_file, "Desktop Entry", "Implements", NULL, NULL);
1151           for (i = 0; implements && implements[i]; i++)
1152             memory_index_add_token (dir->memory_implementations, implements[i], 0, app);
1153           g_strfreev (implements);
1154         }
1155
1156       g_key_file_free (key_file);
1157     }
1158 }
1159
1160 static void
1161 desktop_file_dir_unindexed_search (DesktopFileDir  *dir,
1162                                    const gchar     *search_token)
1163 {
1164   GHashTableIter iter;
1165   gpointer key, value;
1166
1167   if (!dir->memory_index)
1168     desktop_file_dir_unindexed_setup_search (dir);
1169
1170   g_hash_table_iter_init (&iter, dir->memory_index);
1171   while (g_hash_table_iter_next (&iter, &key, &value))
1172     {
1173       MemoryIndexEntry *mie = value;
1174
1175       if (!g_str_has_prefix (key, search_token))
1176         continue;
1177
1178       while (mie)
1179         {
1180           add_token_result (mie->app_name, mie->match_category);
1181           mie = mie->next;
1182         }
1183     }
1184 }
1185
1186 static gboolean
1187 array_contains (GPtrArray *array,
1188                 const gchar *str)
1189 {
1190   gint i;
1191
1192   for (i = 0; i < array->len; i++)
1193     if (g_str_equal (array->pdata[i], str))
1194       return TRUE;
1195
1196   return FALSE;
1197 }
1198
1199 static void
1200 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir *dir,
1201                                         const gchar    *mime_type,
1202                                         GPtrArray      *hits,
1203                                         GPtrArray      *blacklist)
1204 {
1205   UnindexedMimeTweaks *tweaks;
1206   gint i;
1207
1208   tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type);
1209
1210   if (!tweaks)
1211     return;
1212
1213   if (tweaks->additions)
1214     {
1215       for (i = 0; tweaks->additions[i]; i++)
1216         {
1217           gchar *app_name = tweaks->additions[i];
1218
1219           if (!desktop_file_dir_app_name_is_masked (dir, app_name) &&
1220               !array_contains (blacklist, app_name) && !array_contains (hits, app_name))
1221             g_ptr_array_add (hits, app_name);
1222         }
1223     }
1224
1225   if (tweaks->removals)
1226     {
1227       for (i = 0; tweaks->removals[i]; i++)
1228         {
1229           gchar *app_name = tweaks->removals[i];
1230
1231           if (!desktop_file_dir_app_name_is_masked (dir, app_name) &&
1232               !array_contains (blacklist, app_name) && !array_contains (hits, app_name))
1233             g_ptr_array_add (blacklist, app_name);
1234         }
1235     }
1236 }
1237
1238 static void
1239 desktop_file_dir_unindexed_default_lookup (DesktopFileDir *dir,
1240                                            const gchar    *mime_type,
1241                                            GPtrArray      *results)
1242 {
1243   UnindexedMimeTweaks *tweaks;
1244   gint i;
1245
1246   tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type);
1247
1248   if (!tweaks || !tweaks->defaults)
1249     return;
1250
1251   for (i = 0; tweaks->defaults[i]; i++)
1252     {
1253       gchar *app_name = tweaks->defaults[i];
1254
1255       if (!array_contains (results, app_name))
1256         g_ptr_array_add (results, app_name);
1257     }
1258 }
1259
1260 static void
1261 desktop_file_dir_unindexed_get_implementations (DesktopFileDir  *dir,
1262                                                 GList          **results,
1263                                                 const gchar     *interface)
1264 {
1265   MemoryIndexEntry *mie;
1266
1267   if (!dir->memory_index)
1268     desktop_file_dir_unindexed_setup_search (dir);
1269
1270   for (mie = g_hash_table_lookup (dir->memory_implementations, interface); mie; mie = mie->next)
1271     *results = g_list_prepend (*results, g_strdup (mie->app_name));
1272 }
1273
1274 /* DesktopFileDir "API" {{{2 */
1275
1276 /*< internal >
1277  * desktop_file_dir_new:
1278  * @data_dir: an XDG_DATA_DIR
1279  *
1280  * Creates a #DesktopFileDir for the corresponding @data_dir.
1281  */
1282 static DesktopFileDir *
1283 desktop_file_dir_new (const gchar *data_dir)
1284 {
1285   DesktopFileDir *dir = g_new0 (DesktopFileDir, 1);
1286
1287   g_atomic_ref_count_init (&dir->ref_count);
1288   dir->path = g_build_filename (data_dir, "applications", NULL);
1289
1290   return g_steal_pointer (&dir);
1291 }
1292
1293 /*< internal >
1294  * desktop_file_dir_new_for_config:
1295  * @config_dir: an XDG_CONFIG_DIR
1296  *
1297  * Just the same as desktop_file_dir_new() except that it does not
1298  * add the "applications" directory.  It also marks the directory as
1299  * config-only, which prevents us from attempting to find desktop files
1300  * here.
1301  */
1302 static DesktopFileDir *
1303 desktop_file_dir_new_for_config (const gchar *config_dir)
1304 {
1305   DesktopFileDir *dir = g_new0 (DesktopFileDir, 1);
1306
1307   g_atomic_ref_count_init (&dir->ref_count);
1308   dir->path = g_strdup (config_dir);
1309   dir->is_config = TRUE;
1310
1311   return g_steal_pointer (&dir);
1312 }
1313
1314 /*< internal >
1315  * desktop_file_dir_reset:
1316  * @dir: a #DesktopFileDir
1317  *
1318  * Cleans up @dir, releasing most resources that it was using.
1319  */
1320 static void
1321 desktop_file_dir_reset (DesktopFileDir *dir)
1322 {
1323   if (dir->alternatively_watching)
1324     {
1325       g_free (dir->alternatively_watching);
1326       dir->alternatively_watching = NULL;
1327     }
1328
1329   if (dir->monitor)
1330     {
1331       g_signal_handlers_disconnect_by_func (dir->monitor, desktop_file_dir_changed, dir);
1332       g_file_monitor_cancel (dir->monitor);
1333       g_object_unref (dir->monitor);
1334       dir->monitor = NULL;
1335     }
1336
1337   if (dir->app_names)
1338     {
1339       g_hash_table_unref (dir->app_names);
1340       dir->app_names = NULL;
1341     }
1342
1343   if (dir->memory_index)
1344     {
1345       g_hash_table_unref (dir->memory_index);
1346       dir->memory_index = NULL;
1347     }
1348
1349   if (dir->mime_tweaks)
1350     {
1351       g_hash_table_unref (dir->mime_tweaks);
1352       dir->mime_tweaks = NULL;
1353     }
1354
1355   if (dir->memory_implementations)
1356     {
1357       g_hash_table_unref (dir->memory_implementations);
1358       dir->memory_implementations = NULL;
1359     }
1360
1361   dir->is_setup = FALSE;
1362 }
1363
1364 static void
1365 closure_notify_cb (gpointer  data,
1366                    GClosure *closure)
1367 {
1368   DesktopFileDir *dir = data;
1369   desktop_file_dir_unref (dir);
1370 }
1371
1372 /*< internal >
1373  * desktop_file_dir_init:
1374  * @dir: a #DesktopFileDir
1375  *
1376  * Does initial setup for @dir
1377  *
1378  * You should only call this if @dir is not already setup.
1379  */
1380 static void
1381 desktop_file_dir_init (DesktopFileDir *dir)
1382 {
1383   const gchar *watch_dir;
1384
1385   g_assert (!dir->is_setup);
1386
1387   g_assert (!dir->alternatively_watching);
1388   g_assert (!dir->monitor);
1389
1390   dir->alternatively_watching = desktop_file_dir_get_alternative_dir (dir);
1391   watch_dir = dir->alternatively_watching ? dir->alternatively_watching : dir->path;
1392
1393   /* There is a very thin race here if the watch_dir has been _removed_
1394    * between when we checked for it and when we establish the watch.
1395    * Removes probably don't happen in usual operation, and even if it
1396    * does (and we catch the unlikely race), the only degradation is that
1397    * we will fall back to polling.
1398    */
1399   dir->monitor = g_local_file_monitor_new_in_worker (watch_dir, TRUE, G_FILE_MONITOR_NONE,
1400                                                      desktop_file_dir_changed,
1401                                                      desktop_file_dir_ref (dir),
1402                                                      closure_notify_cb, NULL);
1403
1404   desktop_file_dir_unindexed_init (dir);
1405
1406   dir->is_setup = TRUE;
1407 }
1408
1409 /*< internal >
1410  * desktop_file_dir_get_app:
1411  * @dir: a DesktopFileDir
1412  * @desktop_id: the desktop ID to load
1413  *
1414  * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1415  * within @dir, even if it is hidden.
1416  *
1417  * This function does not check if @desktop_id would be masked by a
1418  * directory with higher precedence.  The caller must do so.
1419  */
1420 static GDesktopAppInfo *
1421 desktop_file_dir_get_app (DesktopFileDir *dir,
1422                           const gchar    *desktop_id)
1423 {
1424   if (!dir->app_names)
1425     return NULL;
1426
1427   return desktop_file_dir_unindexed_get_app (dir, desktop_id);
1428 }
1429
1430 /*< internal >
1431  * desktop_file_dir_get_all:
1432  * @dir: a DesktopFileDir
1433  * @apps: a #GHashTable<string, GDesktopAppInfo>
1434  *
1435  * Loads all desktop files in @dir and adds them to @apps, careful to
1436  * ensure we don't add any files masked by a similarly-named file in a
1437  * higher-precedence directory.
1438  */
1439 static void
1440 desktop_file_dir_get_all (DesktopFileDir *dir,
1441                           GHashTable     *apps)
1442 {
1443   desktop_file_dir_unindexed_get_all (dir, apps);
1444 }
1445
1446 /*< internal >
1447  * desktop_file_dir_mime_lookup:
1448  * @dir: a #DesktopFileDir
1449  * @mime_type: the mime type to look up
1450  * @hits: the array to store the hits
1451  * @blacklist: the array to store the blacklist
1452  *
1453  * Does a lookup of a mimetype against one desktop file directory,
1454  * recording any hits and blacklisting and "Removed" associations (so
1455  * later directories don't record them as hits).
1456  *
1457  * The items added to @hits are duplicated, but the ones in @blacklist
1458  * are weak pointers.  This facilitates simply freeing the blacklist
1459  * (which is only used for internal bookkeeping) but using the pdata of
1460  * @hits as the result of the operation.
1461  */
1462 static void
1463 desktop_file_dir_mime_lookup (DesktopFileDir *dir,
1464                               const gchar    *mime_type,
1465                               GPtrArray      *hits,
1466                               GPtrArray      *blacklist)
1467 {
1468   desktop_file_dir_unindexed_mime_lookup (dir, mime_type, hits, blacklist);
1469 }
1470
1471 /*< internal >
1472  * desktop_file_dir_default_lookup:
1473  * @dir: a #DesktopFileDir
1474  * @mime_type: the mime type to look up
1475  * @results: an array to store the results in
1476  *
1477  * Collects the "default" applications for a given mime type from @dir.
1478  */
1479 static void
1480 desktop_file_dir_default_lookup (DesktopFileDir *dir,
1481                                  const gchar    *mime_type,
1482                                  GPtrArray      *results)
1483 {
1484   desktop_file_dir_unindexed_default_lookup (dir, mime_type, results);
1485 }
1486
1487 /*< internal >
1488  * desktop_file_dir_search:
1489  * @dir: a #DesktopFileDir
1490  * @term: a normalised and casefolded search term
1491  *
1492  * Finds the names of applications in @dir that match @term.
1493  */
1494 static void
1495 desktop_file_dir_search (DesktopFileDir *dir,
1496                          const gchar    *search_token)
1497 {
1498   desktop_file_dir_unindexed_search (dir, search_token);
1499 }
1500
1501 static void
1502 desktop_file_dir_get_implementations (DesktopFileDir  *dir,
1503                                       GList          **results,
1504                                       const gchar     *interface)
1505 {
1506   desktop_file_dir_unindexed_get_implementations (dir, results, interface);
1507 }
1508
1509 /* Lock/unlock and global setup API {{{2 */
1510
1511 static void
1512 desktop_file_dirs_lock (void)
1513 {
1514   gint i;
1515   const gchar *user_config_dir = g_get_user_config_dir ();
1516
1517   g_mutex_lock (&desktop_file_dir_lock);
1518
1519   /* If the XDG dirs configuration has changed (expected only during tests),
1520    * clear and reload the state. */
1521   if (desktop_file_dirs_config_dir != NULL &&
1522       g_strcmp0 (desktop_file_dirs_config_dir, user_config_dir) != 0)
1523     {
1524       g_debug ("%s: Resetting desktop app info dirs from %s to %s",
1525                G_STRFUNC, desktop_file_dirs_config_dir, user_config_dir);
1526
1527       g_ptr_array_set_size (desktop_file_dirs, 0);
1528       g_clear_pointer (&desktop_file_dir_user_config, desktop_file_dir_unref);
1529       g_clear_pointer (&desktop_file_dir_user_data, desktop_file_dir_unref);
1530     }
1531
1532   if (desktop_file_dirs == NULL || desktop_file_dirs->len == 0)
1533     {
1534       const char * const *dirs;
1535       gint i;
1536
1537       if (desktop_file_dirs == NULL)
1538         desktop_file_dirs = g_ptr_array_new_with_free_func ((GDestroyNotify) desktop_file_dir_unref);
1539
1540       /* First, the configs.  Highest priority: the user's ~/.config */
1541       desktop_file_dir_user_config = desktop_file_dir_new_for_config (user_config_dir);
1542       g_ptr_array_add (desktop_file_dirs, desktop_file_dir_ref (desktop_file_dir_user_config));
1543
1544       /* Next, the system configs (/etc/xdg, and so on). */
1545       dirs = g_get_system_config_dirs ();
1546       for (i = 0; dirs[i]; i++)
1547         g_ptr_array_add (desktop_file_dirs, desktop_file_dir_new_for_config (dirs[i]));
1548
1549       /* Now the data.  Highest priority: the user's ~/.local/share/applications */
1550       desktop_file_dir_user_data = desktop_file_dir_new (g_get_user_data_dir ());
1551       g_ptr_array_add (desktop_file_dirs, desktop_file_dir_ref (desktop_file_dir_user_data));
1552
1553       /* Following that, XDG_DATA_DIRS/applications, in order */
1554       dirs = g_get_system_data_dirs ();
1555       for (i = 0; dirs[i]; i++)
1556         g_ptr_array_add (desktop_file_dirs, desktop_file_dir_new (dirs[i]));
1557
1558       /* The list of directories will never change after this, unless
1559        * g_get_user_config_dir() changes due to %G_TEST_OPTION_ISOLATE_DIRS. */
1560       desktop_file_dirs_config_dir = user_config_dir;
1561     }
1562
1563   for (i = 0; i < desktop_file_dirs->len; i++)
1564     if (!((DesktopFileDir *) g_ptr_array_index (desktop_file_dirs, i))->is_setup)
1565       desktop_file_dir_init (g_ptr_array_index (desktop_file_dirs, i));
1566 }
1567
1568 static void
1569 desktop_file_dirs_unlock (void)
1570 {
1571   g_mutex_unlock (&desktop_file_dir_lock);
1572 }
1573
1574 static void
1575 desktop_file_dirs_invalidate_user_config (void)
1576 {
1577   g_mutex_lock (&desktop_file_dir_lock);
1578
1579   if (desktop_file_dir_user_config != NULL)
1580     desktop_file_dir_reset (desktop_file_dir_user_config);
1581
1582   g_mutex_unlock (&desktop_file_dir_lock);
1583 }
1584
1585 static void
1586 desktop_file_dirs_invalidate_user_data (void)
1587 {
1588   g_mutex_lock (&desktop_file_dir_lock);
1589
1590   if (desktop_file_dir_user_data != NULL)
1591     desktop_file_dir_reset (desktop_file_dir_user_data);
1592
1593   g_mutex_unlock (&desktop_file_dir_lock);
1594 }
1595
1596 /* GDesktopAppInfo implementation {{{1 */
1597 /* GObject implementation {{{2 */
1598 static void
1599 g_desktop_app_info_finalize (GObject *object)
1600 {
1601   GDesktopAppInfo *info;
1602
1603   info = G_DESKTOP_APP_INFO (object);
1604
1605   g_free (info->desktop_id);
1606   g_free (info->filename);
1607
1608   if (info->keyfile)
1609     g_key_file_unref (info->keyfile);
1610
1611   g_free (info->name);
1612   g_free (info->generic_name);
1613   g_free (info->fullname);
1614   g_free (info->comment);
1615   g_free (info->icon_name);
1616   if (info->icon)
1617     g_object_unref (info->icon);
1618   g_strfreev (info->keywords);
1619   g_strfreev (info->only_show_in);
1620   g_strfreev (info->not_show_in);
1621   g_free (info->try_exec);
1622   g_free (info->exec);
1623   g_free (info->binary);
1624   g_free (info->path);
1625   g_free (info->categories);
1626   g_free (info->startup_wm_class);
1627   g_strfreev (info->mime_types);
1628   g_free (info->app_id);
1629   g_strfreev (info->actions);
1630
1631   G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
1632 }
1633
1634 static void
1635 g_desktop_app_info_set_property (GObject      *object,
1636                                  guint         prop_id,
1637                                  const GValue *value,
1638                                  GParamSpec   *pspec)
1639 {
1640   GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
1641
1642   switch (prop_id)
1643     {
1644     case PROP_FILENAME:
1645       self->filename = g_value_dup_string (value);
1646       break;
1647
1648     default:
1649       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1650       break;
1651     }
1652 }
1653
1654 static void
1655 g_desktop_app_info_get_property (GObject    *object,
1656                                  guint       prop_id,
1657                                  GValue     *value,
1658                                  GParamSpec *pspec)
1659 {
1660   GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
1661
1662   switch (prop_id)
1663     {
1664     case PROP_FILENAME:
1665       g_value_set_string (value, self->filename);
1666       break;
1667     default:
1668       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1669       break;
1670     }
1671 }
1672
1673 static void
1674 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
1675 {
1676   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1677
1678   gobject_class->get_property = g_desktop_app_info_get_property;
1679   gobject_class->set_property = g_desktop_app_info_set_property;
1680   gobject_class->finalize = g_desktop_app_info_finalize;
1681
1682   /**
1683    * GDesktopAppInfo:filename:
1684    *
1685    * The origin filename of this #GDesktopAppInfo
1686    */
1687   g_object_class_install_property (gobject_class,
1688                                    PROP_FILENAME,
1689                                    g_param_spec_string ("filename", "Filename", "", NULL,
1690                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1691 }
1692
1693 static void
1694 g_desktop_app_info_init (GDesktopAppInfo *local)
1695 {
1696 }
1697
1698 /* Construction... {{{2 */
1699
1700 /*< internal >
1701  * binary_from_exec:
1702  * @exec: an exec line
1703  *
1704  * Returns the first word in an exec line (ie: the binary name).
1705  *
1706  * If @exec is "  progname --foo %F" then returns "progname".
1707  */
1708 static char *
1709 binary_from_exec (const char *exec)
1710 {
1711   const char *p, *start;
1712
1713   p = exec;
1714   while (*p == ' ')
1715     p++;
1716   start = p;
1717   while (*p != ' ' && *p != 0)
1718     p++;
1719
1720   return g_strndup (start, p - start);
1721 }
1722
1723 static gboolean
1724 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
1725                                       GKeyFile        *key_file)
1726 {
1727   char *start_group;
1728   char *type;
1729   char *try_exec;
1730   char *exec;
1731   gboolean bus_activatable;
1732
1733   start_group = g_key_file_get_start_group (key_file);
1734   if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
1735     {
1736       g_free (start_group);
1737       return FALSE;
1738     }
1739   g_free (start_group);
1740
1741   type = g_key_file_get_string (key_file,
1742                                 G_KEY_FILE_DESKTOP_GROUP,
1743                                 G_KEY_FILE_DESKTOP_KEY_TYPE,
1744                                 NULL);
1745   if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
1746     {
1747       g_free (type);
1748       return FALSE;
1749     }
1750   g_free (type);
1751
1752   try_exec = g_key_file_get_string (key_file,
1753                                     G_KEY_FILE_DESKTOP_GROUP,
1754                                     G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
1755                                     NULL);
1756   if (try_exec && try_exec[0] != '\0')
1757     {
1758       char *t;
1759       t = g_find_program_in_path (try_exec);
1760       if (t == NULL)
1761         {
1762           g_free (try_exec);
1763           return FALSE;
1764         }
1765       g_free (t);
1766     }
1767
1768   exec = g_key_file_get_string (key_file,
1769                                 G_KEY_FILE_DESKTOP_GROUP,
1770                                 G_KEY_FILE_DESKTOP_KEY_EXEC,
1771                                 NULL);
1772   if (exec && exec[0] != '\0')
1773     {
1774       gint argc;
1775       char **argv;
1776       if (!g_shell_parse_argv (exec, &argc, &argv, NULL))
1777         {
1778           g_free (exec);
1779           g_free (try_exec);
1780           return FALSE;
1781         }
1782       else
1783         {
1784           char *t;
1785           t = g_find_program_in_path (argv[0]);
1786           g_strfreev (argv);
1787
1788           if (t == NULL)
1789             {
1790               g_free (exec);
1791               g_free (try_exec);
1792               return FALSE;
1793             }
1794           g_free (t);
1795         }
1796     }
1797
1798   info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
1799   info->generic_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, GENERIC_NAME_KEY, NULL, NULL);
1800   info->fullname = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, FULL_NAME_KEY, NULL, NULL);
1801   info->keywords = g_key_file_get_locale_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, KEYWORDS_KEY, NULL, NULL, NULL);
1802   info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
1803   info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
1804   info->icon_name =  g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
1805   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);
1806   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);
1807   info->try_exec = try_exec;
1808   info->exec = exec;
1809   info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
1810   info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
1811   info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
1812   info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
1813   info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
1814   info->categories = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL);
1815   info->startup_wm_class = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, STARTUP_WM_CLASS_KEY, NULL);
1816   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);
1817   bus_activatable = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE, NULL);
1818   info->actions = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ACTIONS, NULL, NULL);
1819
1820   /* Remove the special-case: no Actions= key just means 0 extra actions */
1821   if (info->actions == NULL)
1822     info->actions = g_new0 (gchar *, 0 + 1);
1823
1824   info->icon = NULL;
1825   if (info->icon_name)
1826     {
1827       if (g_path_is_absolute (info->icon_name))
1828         {
1829           GFile *file;
1830
1831           file = g_file_new_for_path (info->icon_name);
1832           info->icon = g_file_icon_new (file);
1833           g_object_unref (file);
1834         }
1835       else
1836         {
1837           char *p;
1838
1839           /* Work around a common mistake in desktop files */
1840           if ((p = strrchr (info->icon_name, '.')) != NULL &&
1841               (strcmp (p, ".png") == 0 ||
1842                strcmp (p, ".xpm") == 0 ||
1843                strcmp (p, ".svg") == 0))
1844             *p = 0;
1845
1846           info->icon = g_themed_icon_new (info->icon_name);
1847         }
1848     }
1849
1850   if (info->exec)
1851     info->binary = binary_from_exec (info->exec);
1852
1853   if (info->path && info->path[0] == '\0')
1854     {
1855       g_free (info->path);
1856       info->path = NULL;
1857     }
1858
1859   /* Can only be DBusActivatable if we know the filename, which means
1860    * that this won't work for the load-from-keyfile case.
1861    */
1862   if (bus_activatable && info->filename)
1863     {
1864       gchar *basename;
1865       gchar *last_dot;
1866
1867       basename = g_path_get_basename (info->filename);
1868       last_dot = strrchr (basename, '.');
1869
1870       if (last_dot && g_str_equal (last_dot, ".desktop"))
1871         {
1872           *last_dot = '\0';
1873
1874           if (g_dbus_is_name (basename) && basename[0] != ':')
1875             info->app_id = g_strdup (basename);
1876         }
1877
1878       g_free (basename);
1879     }
1880
1881   info->keyfile = g_key_file_ref (key_file);
1882
1883   return TRUE;
1884 }
1885
1886 static gboolean
1887 g_desktop_app_info_load_file (GDesktopAppInfo *self)
1888 {
1889   GKeyFile *key_file;
1890   gboolean retval = FALSE;
1891
1892   g_return_val_if_fail (self->filename != NULL, FALSE);
1893
1894   self->desktop_id = g_path_get_basename (self->filename);
1895
1896   key_file = g_key_file_new ();
1897
1898   if (g_key_file_load_from_file (key_file, self->filename, G_KEY_FILE_NONE, NULL))
1899     retval = g_desktop_app_info_load_from_keyfile (self, key_file);
1900
1901   g_key_file_unref (key_file);
1902   return retval;
1903 }
1904
1905 /**
1906  * g_desktop_app_info_new_from_keyfile:
1907  * @key_file: an opened #GKeyFile
1908  *
1909  * Creates a new #GDesktopAppInfo.
1910  *
1911  * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error.
1912  *
1913  * Since: 2.18
1914  **/
1915 GDesktopAppInfo *
1916 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
1917 {
1918   GDesktopAppInfo *info;
1919
1920   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1921   info->filename = NULL;
1922   if (!g_desktop_app_info_load_from_keyfile (info, key_file))
1923     {
1924       g_object_unref (info);
1925       return NULL;
1926     }
1927   return info;
1928 }
1929
1930 /**
1931  * g_desktop_app_info_new_from_filename:
1932  * @filename: (type filename): the path of a desktop file, in the GLib
1933  *      filename encoding
1934  *
1935  * Creates a new #GDesktopAppInfo.
1936  *
1937  * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error.
1938  **/
1939 GDesktopAppInfo *
1940 g_desktop_app_info_new_from_filename (const char *filename)
1941 {
1942   GDesktopAppInfo *info = NULL;
1943
1944   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
1945   if (!g_desktop_app_info_load_file (info))
1946     {
1947       g_object_unref (info);
1948       return NULL;
1949     }
1950   return info;
1951 }
1952
1953 /**
1954  * g_desktop_app_info_new:
1955  * @desktop_id: the desktop file id
1956  *
1957  * Creates a new #GDesktopAppInfo based on a desktop file id.
1958  *
1959  * A desktop file id is the basename of the desktop file, including the
1960  * .desktop extension. GIO is looking for a desktop file with this name
1961  * in the `applications` subdirectories of the XDG
1962  * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1963  * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1964  * prefix-to-subdirectory mapping that is described in the
1965  * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1966  * (i.e. a desktop id of kde-foo.desktop will match
1967  * `/usr/share/applications/kde/foo.desktop`).
1968  *
1969  * Returns: (nullable): a new #GDesktopAppInfo, or %NULL if no desktop
1970  *     file with that id exists.
1971  */
1972 GDesktopAppInfo *
1973 g_desktop_app_info_new (const char *desktop_id)
1974 {
1975   GDesktopAppInfo *appinfo = NULL;
1976   guint i;
1977
1978   desktop_file_dirs_lock ();
1979
1980   for (i = 0; i < desktop_file_dirs->len; i++)
1981     {
1982       appinfo = desktop_file_dir_get_app (g_ptr_array_index (desktop_file_dirs, i), desktop_id);
1983
1984       if (appinfo)
1985         break;
1986     }
1987
1988   desktop_file_dirs_unlock ();
1989
1990   if (appinfo == NULL)
1991     return NULL;
1992
1993   g_free (appinfo->desktop_id);
1994   appinfo->desktop_id = g_strdup (desktop_id);
1995
1996   if (g_desktop_app_info_get_is_hidden (appinfo))
1997     {
1998       g_object_unref (appinfo);
1999       appinfo = NULL;
2000     }
2001
2002   return appinfo;
2003 }
2004
2005 static GAppInfo *
2006 g_desktop_app_info_dup (GAppInfo *appinfo)
2007 {
2008   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2009   GDesktopAppInfo *new_info;
2010
2011   new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
2012
2013   new_info->filename = g_strdup (info->filename);
2014   new_info->desktop_id = g_strdup (info->desktop_id);
2015
2016   if (info->keyfile)
2017     new_info->keyfile = g_key_file_ref (info->keyfile);
2018
2019   new_info->name = g_strdup (info->name);
2020   new_info->generic_name = g_strdup (info->generic_name);
2021   new_info->fullname = g_strdup (info->fullname);
2022   new_info->keywords = g_strdupv (info->keywords);
2023   new_info->comment = g_strdup (info->comment);
2024   new_info->nodisplay = info->nodisplay;
2025   new_info->icon_name = g_strdup (info->icon_name);
2026   if (info->icon)
2027     new_info->icon = g_object_ref (info->icon);
2028   new_info->only_show_in = g_strdupv (info->only_show_in);
2029   new_info->not_show_in = g_strdupv (info->not_show_in);
2030   new_info->try_exec = g_strdup (info->try_exec);
2031   new_info->exec = g_strdup (info->exec);
2032   new_info->binary = g_strdup (info->binary);
2033   new_info->path = g_strdup (info->path);
2034   new_info->app_id = g_strdup (info->app_id);
2035   new_info->hidden = info->hidden;
2036   new_info->terminal = info->terminal;
2037   new_info->startup_notify = info->startup_notify;
2038
2039   return G_APP_INFO (new_info);
2040 }
2041
2042 /* GAppInfo interface implementation functions {{{2 */
2043
2044 static gboolean
2045 g_desktop_app_info_equal (GAppInfo *appinfo1,
2046                           GAppInfo *appinfo2)
2047 {
2048   GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
2049   GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
2050
2051   if (info1->desktop_id == NULL ||
2052       info2->desktop_id == NULL)
2053     return info1 == info2;
2054
2055   return strcmp (info1->desktop_id, info2->desktop_id) == 0;
2056 }
2057
2058 static const char *
2059 g_desktop_app_info_get_id (GAppInfo *appinfo)
2060 {
2061   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2062
2063   return info->desktop_id;
2064 }
2065
2066 static const char *
2067 g_desktop_app_info_get_name (GAppInfo *appinfo)
2068 {
2069   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2070
2071   if (info->name == NULL)
2072     return _("Unnamed");
2073   return info->name;
2074 }
2075
2076 static const char *
2077 g_desktop_app_info_get_display_name (GAppInfo *appinfo)
2078 {
2079   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2080
2081   if (info->fullname == NULL)
2082     return g_desktop_app_info_get_name (appinfo);
2083   return info->fullname;
2084 }
2085
2086 /**
2087  * g_desktop_app_info_get_is_hidden:
2088  * @info: a #GDesktopAppInfo.
2089  *
2090  * A desktop file is hidden if the Hidden key in it is
2091  * set to True.
2092  *
2093  * Returns: %TRUE if hidden, %FALSE otherwise.
2094  **/
2095 gboolean
2096 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
2097 {
2098   return info->hidden;
2099 }
2100
2101 /**
2102  * g_desktop_app_info_get_filename:
2103  * @info: a #GDesktopAppInfo
2104  *
2105  * When @info was created from a known filename, return it.  In some
2106  * situations such as the #GDesktopAppInfo returned from
2107  * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
2108  *
2109  * Returns: (type filename): The full path to the file for @info,
2110  *     or %NULL if not known.
2111  * Since: 2.24
2112  */
2113 const char *
2114 g_desktop_app_info_get_filename (GDesktopAppInfo *info)
2115 {
2116   return info->filename;
2117 }
2118
2119 static const char *
2120 g_desktop_app_info_get_description (GAppInfo *appinfo)
2121 {
2122   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2123
2124   return info->comment;
2125 }
2126
2127 static const char *
2128 g_desktop_app_info_get_executable (GAppInfo *appinfo)
2129 {
2130   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2131
2132   return info->binary;
2133 }
2134
2135 static const char *
2136 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
2137 {
2138   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2139
2140   return info->exec;
2141 }
2142
2143 static GIcon *
2144 g_desktop_app_info_get_icon (GAppInfo *appinfo)
2145 {
2146   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2147
2148   return info->icon;
2149 }
2150
2151 /**
2152  * g_desktop_app_info_get_categories:
2153  * @info: a #GDesktopAppInfo
2154  *
2155  * Gets the categories from the desktop file.
2156  *
2157  * Returns: The unparsed Categories key from the desktop file;
2158  *     i.e. no attempt is made to split it by ';' or validate it.
2159  */
2160 const char *
2161 g_desktop_app_info_get_categories (GDesktopAppInfo *info)
2162 {
2163   return info->categories;
2164 }
2165
2166 /**
2167  * g_desktop_app_info_get_keywords:
2168  * @info: a #GDesktopAppInfo
2169  *
2170  * Gets the keywords from the desktop file.
2171  *
2172  * Returns: (transfer none): The value of the Keywords key
2173  *
2174  * Since: 2.32
2175  */
2176 const char * const *
2177 g_desktop_app_info_get_keywords (GDesktopAppInfo *info)
2178 {
2179   return (const char * const *)info->keywords;
2180 }
2181
2182 /**
2183  * g_desktop_app_info_get_generic_name:
2184  * @info: a #GDesktopAppInfo
2185  *
2186  * Gets the generic name from the destkop file.
2187  *
2188  * Returns: The value of the GenericName key
2189  */
2190 const char *
2191 g_desktop_app_info_get_generic_name (GDesktopAppInfo *info)
2192 {
2193   return info->generic_name;
2194 }
2195
2196 /**
2197  * g_desktop_app_info_get_nodisplay:
2198  * @info: a #GDesktopAppInfo
2199  *
2200  * Gets the value of the NoDisplay key, which helps determine if the
2201  * application info should be shown in menus. See
2202  * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2203  *
2204  * Returns: The value of the NoDisplay key
2205  *
2206  * Since: 2.30
2207  */
2208 gboolean
2209 g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info)
2210 {
2211   return info->nodisplay;
2212 }
2213
2214 /**
2215  * g_desktop_app_info_get_show_in:
2216  * @info: a #GDesktopAppInfo
2217  * @desktop_env: (nullable): a string specifying a desktop name
2218  *
2219  * Checks if the application info should be shown in menus that list available
2220  * applications for a specific name of the desktop, based on the
2221  * `OnlyShowIn` and `NotShowIn` keys.
2222  *
2223  * @desktop_env should typically be given as %NULL, in which case the
2224  * `XDG_CURRENT_DESKTOP` environment variable is consulted.  If you want
2225  * to override the default mechanism then you may specify @desktop_env,
2226  * but this is not recommended.
2227  *
2228  * Note that g_app_info_should_show() for @info will include this check (with
2229  * %NULL for @desktop_env) as well as additional checks.
2230  *
2231  * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2232  * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2233  * otherwise.
2234  *
2235  * Since: 2.30
2236  */
2237 gboolean
2238 g_desktop_app_info_get_show_in (GDesktopAppInfo *info,
2239                                 const gchar     *desktop_env)
2240 {
2241   const gchar *specified_envs[] = { desktop_env, NULL };
2242   const gchar * const *envs;
2243   gint i;
2244
2245   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
2246
2247   if (desktop_env)
2248     envs = specified_envs;
2249   else
2250     envs = get_current_desktops (NULL);
2251
2252   for (i = 0; envs[i]; i++)
2253     {
2254       gint j;
2255
2256       if (info->only_show_in)
2257         for (j = 0; info->only_show_in[j]; j++)
2258           if (g_str_equal (info->only_show_in[j], envs[i]))
2259             return TRUE;
2260
2261       if (info->not_show_in)
2262         for (j = 0; info->not_show_in[j]; j++)
2263           if (g_str_equal (info->not_show_in[j], envs[i]))
2264             return FALSE;
2265     }
2266
2267   return info->only_show_in == NULL;
2268 }
2269
2270 /* Launching... {{{2 */
2271
2272 static char *
2273 expand_macro_single (char macro, const char *uri)
2274 {
2275   GFile *file;
2276   char *result = NULL;
2277   char *path = NULL;
2278   char *name;
2279
2280   file = g_file_new_for_uri (uri);
2281
2282   switch (macro)
2283     {
2284     case 'u':
2285     case 'U':
2286       result = g_shell_quote (uri);
2287       break;
2288     case 'f':
2289     case 'F':
2290       path = g_file_get_path (file);
2291       if (path)
2292         result = g_shell_quote (path);
2293       break;
2294     case 'd':
2295     case 'D':
2296       path = g_file_get_path (file);
2297       if (path)
2298         {
2299           name = g_path_get_dirname (path);
2300           result = g_shell_quote (name);
2301           g_free (name);
2302         }
2303       break;
2304     case 'n':
2305     case 'N':
2306       path = g_file_get_path (file);
2307       if (path)
2308         {
2309           name = g_path_get_basename (path);
2310           result = g_shell_quote (name);
2311           g_free (name);
2312         }
2313       break;
2314     }
2315
2316   g_object_unref (file);
2317   g_free (path);
2318
2319   return result;
2320 }
2321
2322 static char *
2323 expand_macro_uri (char macro, const char *uri, gboolean force_file_uri, char force_file_uri_macro)
2324 {
2325   char *expanded = NULL;
2326
2327   g_return_val_if_fail (uri != NULL, NULL);
2328
2329   if (!force_file_uri ||
2330       /* Pass URI if it contains an anchor */
2331       strchr (uri, '#') != NULL)
2332     {
2333       expanded = expand_macro_single (macro, uri);
2334     }
2335   else
2336     {
2337       expanded = expand_macro_single (force_file_uri_macro, uri);
2338       if (expanded == NULL)
2339         expanded = expand_macro_single (macro, uri);
2340     }
2341
2342   return expanded;
2343 }
2344
2345 static void
2346 expand_macro (char              macro,
2347               GString          *exec,
2348               GDesktopAppInfo  *info,
2349               GList           **uri_list)
2350 {
2351   GList *uris = *uri_list;
2352   char *expanded = NULL;
2353   gboolean force_file_uri;
2354   char force_file_uri_macro;
2355   const char *uri;
2356
2357   g_return_if_fail (exec != NULL);
2358
2359   /* On %u and %U, pass POSIX file path pointing to the URI via
2360    * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2361    * running or the URI doesn't have a POSIX file path via FUSE
2362    * we'll just pass the URI.
2363    */
2364   force_file_uri_macro = macro;
2365   force_file_uri = FALSE;
2366   if (!info->no_fuse)
2367     {
2368       switch (macro)
2369         {
2370         case 'u':
2371           force_file_uri_macro = 'f';
2372           force_file_uri = TRUE;
2373           break;
2374         case 'U':
2375           force_file_uri_macro = 'F';
2376           force_file_uri = TRUE;
2377           break;
2378         default:
2379           break;
2380         }
2381     }
2382
2383   switch (macro)
2384     {
2385     case 'u':
2386     case 'f':
2387     case 'd':
2388     case 'n':
2389       if (uris)
2390         {
2391           uri = uris->data;
2392           expanded = expand_macro_uri (macro, uri,
2393                                        force_file_uri, force_file_uri_macro);
2394           if (expanded)
2395             {
2396               g_string_append (exec, expanded);
2397               g_free (expanded);
2398             }
2399           uris = uris->next;
2400         }
2401
2402       break;
2403
2404     case 'U':
2405     case 'F':
2406     case 'D':
2407     case 'N':
2408       while (uris)
2409         {
2410           uri = uris->data;
2411           expanded = expand_macro_uri (macro, uri,
2412                                        force_file_uri, force_file_uri_macro);
2413           if (expanded)
2414             {
2415               g_string_append (exec, expanded);
2416               g_free (expanded);
2417             }
2418
2419           uris = uris->next;
2420
2421           if (uris != NULL && expanded)
2422             g_string_append_c (exec, ' ');
2423         }
2424
2425       break;
2426
2427     case 'i':
2428       if (info->icon_name)
2429         {
2430           g_string_append (exec, "--icon ");
2431           expanded = g_shell_quote (info->icon_name);
2432           g_string_append (exec, expanded);
2433           g_free (expanded);
2434         }
2435       break;
2436
2437     case 'c':
2438       if (info->name)
2439         {
2440           expanded = g_shell_quote (info->name);
2441           g_string_append (exec, expanded);
2442           g_free (expanded);
2443         }
2444       break;
2445
2446     case 'k':
2447       if (info->filename)
2448         {
2449           expanded = g_shell_quote (info->filename);
2450           g_string_append (exec, expanded);
2451           g_free (expanded);
2452         }
2453       break;
2454
2455     case 'm': /* deprecated */
2456       break;
2457
2458     case '%':
2459       g_string_append_c (exec, '%');
2460       break;
2461     }
2462
2463   *uri_list = uris;
2464 }
2465
2466 static gboolean
2467 expand_application_parameters (GDesktopAppInfo   *info,
2468                                const gchar       *exec_line,
2469                                GList            **uris,
2470                                int               *argc,
2471                                char            ***argv,
2472                                GError           **error)
2473 {
2474   GList *uri_list = *uris;
2475   const char *p = exec_line;
2476   GString *expanded_exec;
2477   gboolean res;
2478
2479   if (exec_line == NULL)
2480     {
2481       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2482                            _("Desktop file didn’t specify Exec field"));
2483       return FALSE;
2484     }
2485
2486   expanded_exec = g_string_new (NULL);
2487
2488   while (*p)
2489     {
2490       if (p[0] == '%' && p[1] != '\0')
2491         {
2492           expand_macro (p[1], expanded_exec, info, uris);
2493           p++;
2494         }
2495       else
2496         g_string_append_c (expanded_exec, *p);
2497
2498       p++;
2499     }
2500
2501   /* No file substitutions */
2502   if (uri_list == *uris && uri_list != NULL)
2503     {
2504       /* If there is no macro default to %f. This is also what KDE does */
2505       g_string_append_c (expanded_exec, ' ');
2506       expand_macro ('f', expanded_exec, info, uris);
2507     }
2508
2509   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
2510   g_string_free (expanded_exec, TRUE);
2511   return res;
2512 }
2513
2514 static gboolean
2515 prepend_terminal_to_vector (int    *argc,
2516                             char ***argv)
2517 {
2518 #ifndef G_OS_WIN32
2519   char **real_argv;
2520   int real_argc;
2521   int i, j;
2522   char **term_argv = NULL;
2523   int term_argc = 0;
2524   char *check;
2525   char **the_argv;
2526
2527   g_return_val_if_fail (argc != NULL, FALSE);
2528   g_return_val_if_fail (argv != NULL, FALSE);
2529
2530   /* sanity */
2531   if(*argv == NULL)
2532     *argc = 0;
2533
2534   the_argv = *argv;
2535
2536   /* compute size if not given */
2537   if (*argc < 0)
2538     {
2539       for (i = 0; the_argv[i] != NULL; i++)
2540         ;
2541       *argc = i;
2542     }
2543
2544   term_argc = 2;
2545   term_argv = g_new0 (char *, 3);
2546
2547   check = g_find_program_in_path ("gnome-terminal");
2548   if (check != NULL)
2549     {
2550       term_argv[0] = check;
2551       /* Since 2017, gnome-terminal has preferred `--` over `-x` or `-e`. */
2552       term_argv[1] = g_strdup ("--");
2553     }
2554   else
2555     {
2556       if (check == NULL)
2557         check = g_find_program_in_path ("mate-terminal");
2558       if (check == NULL)
2559         check = g_find_program_in_path ("xfce4-terminal");
2560       if (check != NULL)
2561         {
2562           term_argv[0] = check;
2563           /* Note that gnome-terminal takes -x and
2564            * as -e in gnome-terminal is broken we use that. */
2565           term_argv[1] = g_strdup ("-x");
2566         }
2567       else
2568         {
2569           if (check == NULL)
2570             check = g_find_program_in_path ("nxterm");
2571           if (check == NULL)
2572             check = g_find_program_in_path ("color-xterm");
2573           if (check == NULL)
2574             check = g_find_program_in_path ("rxvt");
2575           if (check == NULL)
2576             check = g_find_program_in_path ("dtterm");
2577           if (check == NULL)
2578             {
2579               check = g_strdup ("xterm");
2580               g_debug ("Couldn’t find a terminal: falling back to xterm");
2581             }
2582           term_argv[0] = check;
2583           term_argv[1] = g_strdup ("-e");
2584         }
2585     }
2586
2587   real_argc = term_argc + *argc;
2588   real_argv = g_new (char *, real_argc + 1);
2589
2590   for (i = 0; i < term_argc; i++)
2591     real_argv[i] = term_argv[i];
2592
2593   for (j = 0; j < *argc; j++, i++)
2594     real_argv[i] = (char *)the_argv[j];
2595
2596   real_argv[i] = NULL;
2597
2598   g_free (*argv);
2599   *argv = real_argv;
2600   *argc = real_argc;
2601
2602   /* we use g_free here as we sucked all the inner strings
2603    * out from it into real_argv */
2604   g_free (term_argv);
2605   return TRUE;
2606 #else
2607   return FALSE;
2608 #endif /* G_OS_WIN32 */
2609 }
2610
2611 static GList *
2612 create_files_for_uris (GList *uris)
2613 {
2614   GList *res;
2615   GList *iter;
2616
2617   res = NULL;
2618
2619   for (iter = uris; iter; iter = iter->next)
2620     {
2621       GFile *file = g_file_new_for_uri ((char *)iter->data);
2622       res = g_list_prepend (res, file);
2623     }
2624
2625   return g_list_reverse (res);
2626 }
2627
2628 static void
2629 notify_desktop_launch (GDBusConnection  *session_bus,
2630                        GDesktopAppInfo  *info,
2631                        long              pid,
2632                        const char       *display,
2633                        const char       *sn_id,
2634                        GList            *uris)
2635 {
2636   GDBusMessage *msg;
2637   GVariantBuilder uri_variant;
2638   GVariantBuilder extras_variant;
2639   GList *iter;
2640   const char *desktop_file_id;
2641   const char *gio_desktop_file;
2642
2643   if (session_bus == NULL)
2644     return;
2645
2646   g_variant_builder_init (&uri_variant, G_VARIANT_TYPE ("as"));
2647   for (iter = uris; iter; iter = iter->next)
2648     g_variant_builder_add (&uri_variant, "s", iter->data);
2649
2650   g_variant_builder_init (&extras_variant, G_VARIANT_TYPE ("a{sv}"));
2651   if (sn_id != NULL && g_utf8_validate (sn_id, -1, NULL))
2652     g_variant_builder_add (&extras_variant, "{sv}",
2653                            "startup-id",
2654                            g_variant_new ("s",
2655                                           sn_id));
2656   gio_desktop_file = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2657   if (gio_desktop_file != NULL)
2658     g_variant_builder_add (&extras_variant, "{sv}",
2659                            "origin-desktop-file",
2660                            g_variant_new_bytestring (gio_desktop_file));
2661   if (g_get_prgname () != NULL)
2662     g_variant_builder_add (&extras_variant, "{sv}",
2663                            "origin-prgname",
2664                            g_variant_new_bytestring (g_get_prgname ()));
2665   g_variant_builder_add (&extras_variant, "{sv}",
2666                          "origin-pid",
2667                          g_variant_new ("x",
2668                                         (gint64)getpid ()));
2669
2670   if (info->filename)
2671     desktop_file_id = info->filename;
2672   else if (info->desktop_id)
2673     desktop_file_id = info->desktop_id;
2674   else
2675     desktop_file_id = "";
2676
2677   msg = g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2678                                    "org.gtk.gio.DesktopAppInfo",
2679                                    "Launched");
2680   g_dbus_message_set_body (msg, g_variant_new ("(@aysxasa{sv})",
2681                                                g_variant_new_bytestring (desktop_file_id),
2682                                                display ? display : "",
2683                                                (gint64)pid,
2684                                                &uri_variant,
2685                                                &extras_variant));
2686   g_dbus_connection_send_message (session_bus,
2687                                   msg, 0,
2688                                   NULL,
2689                                   NULL);
2690   g_object_unref (msg);
2691 }
2692
2693 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2694
2695 static gboolean
2696 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
2697                                            GDBusConnection            *session_bus,
2698                                            const gchar                *exec_line,
2699                                            GList                      *uris,
2700                                            GAppLaunchContext          *launch_context,
2701                                            GSpawnFlags                 spawn_flags,
2702                                            GSpawnChildSetupFunc        user_setup,
2703                                            gpointer                    user_setup_data,
2704                                            GDesktopAppLaunchCallback   pid_callback,
2705                                            gpointer                    pid_callback_data,
2706                                            gint                        stdin_fd,
2707                                            gint                        stdout_fd,
2708                                            gint                        stderr_fd,
2709                                            GError                    **error)
2710 {
2711   gboolean completed = FALSE;
2712   GList *old_uris;
2713   GList *dup_uris;
2714
2715   char **argv, **envp;
2716   int argc;
2717
2718   g_return_val_if_fail (info != NULL, FALSE);
2719
2720   argv = NULL;
2721
2722   if (launch_context)
2723     envp = g_app_launch_context_get_environment (launch_context);
2724   else
2725     envp = g_get_environ ();
2726
2727   /* The GList* passed to expand_application_parameters() will be modified
2728    * internally by expand_macro(), so we need to pass a copy of it instead,
2729    * and also use that copy to control the exit condition of the loop below.
2730    */
2731   dup_uris = g_list_copy (uris);
2732   do
2733     {
2734       GPid pid;
2735       GList *launched_uris;
2736       GList *iter;
2737       char *sn_id = NULL;
2738       char **wrapped_argv;
2739       int i;
2740
2741       old_uris = dup_uris;
2742       if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
2743         goto out;
2744
2745       /* Get the subset of URIs we're launching with this process */
2746       launched_uris = NULL;
2747       for (iter = old_uris; iter != NULL && iter != dup_uris; iter = iter->next)
2748         launched_uris = g_list_prepend (launched_uris, iter->data);
2749       launched_uris = g_list_reverse (launched_uris);
2750
2751       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
2752         {
2753           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2754                                _("Unable to find terminal required for application"));
2755           goto out;
2756         }
2757
2758       if (info->filename)
2759         envp = g_environ_setenv (envp,
2760                                  "GIO_LAUNCHED_DESKTOP_FILE",
2761                                  info->filename,
2762                                  TRUE);
2763
2764       sn_id = NULL;
2765       if (launch_context)
2766         {
2767           GList *launched_files = create_files_for_uris (launched_uris);
2768
2769           if (info->startup_notify)
2770             {
2771               sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
2772                                                                   G_APP_INFO (info),
2773                                                                   launched_files);
2774               if (sn_id)
2775                 envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE);
2776             }
2777
2778           g_list_free_full (launched_files, g_object_unref);
2779         }
2780
2781       if (g_once_init_enter (&gio_launch_desktop_path))
2782         {
2783           const gchar *tmp;
2784
2785           /* Allow test suite to specify path to gio-launch-desktop */
2786           tmp = g_getenv ("GIO_LAUNCH_DESKTOP");
2787
2788           /* Fall back on usual searching in $PATH */
2789           if (tmp == NULL)
2790             tmp = "gio-launch-desktop";
2791           g_once_init_leave (&gio_launch_desktop_path, tmp);
2792         }
2793
2794       wrapped_argv = g_new (char *, argc + 2);
2795       wrapped_argv[0] = g_strdup (gio_launch_desktop_path);
2796
2797       for (i = 0; i < argc; i++)
2798         wrapped_argv[i + 1] = g_steal_pointer (&argv[i]);
2799
2800       wrapped_argv[i + 1] = NULL;
2801       g_free (argv);
2802       argv = NULL;
2803
2804       if (!g_spawn_async_with_fds (info->path,
2805                                    wrapped_argv,
2806                                    envp,
2807                                    spawn_flags,
2808                                    user_setup,
2809                                    user_setup_data,
2810                                    &pid,
2811                                    stdin_fd,
2812                                    stdout_fd,
2813                                    stderr_fd,
2814                                    error))
2815         {
2816           if (sn_id)
2817             g_app_launch_context_launch_failed (launch_context, sn_id);
2818
2819           g_free (sn_id);
2820           g_list_free (launched_uris);
2821
2822           goto out;
2823         }
2824
2825       if (pid_callback != NULL)
2826         pid_callback (info, pid, pid_callback_data);
2827
2828       if (launch_context != NULL)
2829         {
2830           GVariantBuilder builder;
2831           GVariant *platform_data;
2832
2833           g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
2834           g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 (pid));
2835           if (sn_id)
2836             g_variant_builder_add (&builder, "{sv}", "startup-notification-id", g_variant_new_string (sn_id));
2837           platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
2838           g_signal_emit_by_name (launch_context, "launched", info, platform_data);
2839           g_variant_unref (platform_data);
2840         }
2841
2842       notify_desktop_launch (session_bus,
2843                              info,
2844                              pid,
2845                              NULL,
2846                              sn_id,
2847                              launched_uris);
2848
2849       g_free (sn_id);
2850       g_list_free (launched_uris);
2851
2852       g_strfreev (wrapped_argv);
2853       wrapped_argv = NULL;
2854     }
2855   while (dup_uris != NULL);
2856
2857   completed = TRUE;
2858
2859  out:
2860   g_list_free (dup_uris);
2861   g_strfreev (argv);
2862   g_strfreev (envp);
2863
2864   return completed;
2865 }
2866
2867 static gchar *
2868 object_path_from_appid (const gchar *appid)
2869 {
2870   gchar *appid_path, *iter;
2871
2872   appid_path = g_strconcat ("/", appid, NULL);
2873   for (iter = appid_path; *iter; iter++)
2874     {
2875       if (*iter == '.')
2876         *iter = '/';
2877
2878       if (*iter == '-')
2879         *iter = '_';
2880     }
2881
2882   return appid_path;
2883 }
2884
2885 static GVariant *
2886 g_desktop_app_info_make_platform_data (GDesktopAppInfo   *info,
2887                                        GList             *uris,
2888                                        GAppLaunchContext *launch_context)
2889 {
2890   GVariantBuilder builder;
2891
2892   g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
2893
2894   if (launch_context)
2895     {
2896       GList *launched_files = create_files_for_uris (uris);
2897
2898       if (info->startup_notify)
2899         {
2900           gchar *sn_id;
2901
2902           sn_id = g_app_launch_context_get_startup_notify_id (launch_context, G_APP_INFO (info), launched_files);
2903           if (sn_id)
2904             g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id));
2905         }
2906
2907       g_list_free_full (launched_files, g_object_unref);
2908     }
2909
2910   return g_variant_builder_end (&builder);
2911 }
2912
2913 static void
2914 launch_uris_with_dbus (GDesktopAppInfo    *info,
2915                        GDBusConnection    *session_bus,
2916                        GList              *uris,
2917                        GAppLaunchContext  *launch_context,
2918                        GCancellable       *cancellable,
2919                        GAsyncReadyCallback callback,
2920                        gpointer            user_data)
2921 {
2922   GVariantBuilder builder;
2923   gchar *object_path;
2924
2925   g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
2926
2927   if (uris)
2928     {
2929       GList *iter;
2930
2931       g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
2932       for (iter = uris; iter; iter = iter->next)
2933         g_variant_builder_add (&builder, "s", iter->data);
2934       g_variant_builder_close (&builder);
2935     }
2936
2937   g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
2938
2939   object_path = object_path_from_appid (info->app_id);
2940   g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
2941                           uris ? "Open" : "Activate", g_variant_builder_end (&builder),
2942                           NULL, G_DBUS_CALL_FLAGS_NONE, -1,
2943                           cancellable, callback, user_data);
2944   g_free (object_path);
2945 }
2946
2947 static gboolean
2948 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo    *info,
2949                                           GDBusConnection    *session_bus,
2950                                           GList              *uris,
2951                                           GAppLaunchContext  *launch_context,
2952                                           GCancellable       *cancellable,
2953                                           GAsyncReadyCallback callback,
2954                                           gpointer            user_data)
2955 {
2956   GList *ruris = uris;
2957   char *app_id = NULL;
2958
2959   g_return_val_if_fail (info != NULL, FALSE);
2960
2961 #ifdef G_OS_UNIX
2962   app_id = g_desktop_app_info_get_string (info, "X-Flatpak");
2963   if (app_id && *app_id)
2964     {
2965       ruris = g_document_portal_add_documents (uris, app_id, NULL);
2966       if (ruris == NULL)
2967         ruris = uris;
2968     }
2969 #endif
2970
2971   launch_uris_with_dbus (info, session_bus, ruris, launch_context,
2972                          cancellable, callback, user_data);
2973
2974   if (ruris != uris)
2975     g_list_free_full (ruris, g_free);
2976
2977   g_free (app_id);
2978
2979   return TRUE;
2980 }
2981
2982 static gboolean
2983 g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
2984                                          GList                      *uris,
2985                                          GAppLaunchContext          *launch_context,
2986                                          GSpawnFlags                 spawn_flags,
2987                                          GSpawnChildSetupFunc        user_setup,
2988                                          gpointer                    user_setup_data,
2989                                          GDesktopAppLaunchCallback   pid_callback,
2990                                          gpointer                    pid_callback_data,
2991                                          gint                        stdin_fd,
2992                                          gint                        stdout_fd,
2993                                          gint                        stderr_fd,
2994                                          GError                     **error)
2995 {
2996   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2997   GDBusConnection *session_bus;
2998   gboolean success = TRUE;
2999
3000   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
3001
3002   if (session_bus && info->app_id)
3003     /* This is non-blocking API. Similar to launching via fork()/exec()
3004      * we don't wait around to see if the program crashed during startup.
3005      * This is what startup-notification's job is...
3006      */
3007     g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context,
3008                                               NULL, NULL, NULL);
3009   else
3010     success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
3011                                                          spawn_flags, user_setup, user_setup_data,
3012                                                          pid_callback, pid_callback_data,
3013                                                          stdin_fd, stdout_fd, stderr_fd, error);
3014
3015   if (session_bus != NULL)
3016     {
3017       /* This asynchronous flush holds a reference until it completes,
3018        * which ensures that the following unref won't immediately kill
3019        * the connection if we were the initial owner.
3020        */
3021       g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
3022       g_object_unref (session_bus);
3023     }
3024
3025   return success;
3026 }
3027
3028 static gboolean
3029 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
3030                                 GList              *uris,
3031                                 GAppLaunchContext  *launch_context,
3032                                 GError            **error)
3033 {
3034   return g_desktop_app_info_launch_uris_internal (appinfo, uris,
3035                                                   launch_context,
3036                                                   _SPAWN_FLAGS_DEFAULT,
3037                                                   NULL, NULL, NULL, NULL,
3038                                                   -1, -1, -1,
3039                                                   error);
3040 }
3041
3042 typedef struct
3043 {
3044   GAppInfo *appinfo;
3045   GList *uris;
3046   GAppLaunchContext *context;
3047 } LaunchUrisData;
3048
3049 static void
3050 launch_uris_data_free (LaunchUrisData *data)
3051 {
3052   g_clear_object (&data->context);
3053   g_list_free_full (data->uris, g_free);
3054   g_free (data);
3055 }
3056
3057 static void
3058 launch_uris_with_dbus_cb (GObject      *object,
3059                           GAsyncResult *result,
3060                           gpointer      user_data)
3061 {
3062   GTask *task = G_TASK (user_data);
3063   GError *error = NULL;
3064
3065   g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
3066   if (error != NULL)
3067     {
3068       g_dbus_error_strip_remote_error (error);
3069       g_task_return_error (task, g_steal_pointer (&error));
3070     }
3071   else
3072     g_task_return_boolean (task, TRUE);
3073
3074   g_object_unref (task);
3075 }
3076
3077 static void
3078 launch_uris_flush_cb (GObject      *object,
3079                       GAsyncResult *result,
3080                       gpointer      user_data)
3081 {
3082   GTask *task = G_TASK (user_data);
3083
3084   g_dbus_connection_flush_finish (G_DBUS_CONNECTION (object), result, NULL);
3085   g_task_return_boolean (task, TRUE);
3086   g_object_unref (task);
3087 }
3088
3089 static void
3090 launch_uris_bus_get_cb (GObject      *object,
3091                         GAsyncResult *result,
3092                         gpointer      user_data)
3093 {
3094   GTask *task = G_TASK (user_data);
3095   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (g_task_get_source_object (task));
3096   LaunchUrisData *data = g_task_get_task_data (task);
3097   GCancellable *cancellable = g_task_get_cancellable (task);
3098   GDBusConnection *session_bus;
3099   GError *error = NULL;
3100
3101   session_bus = g_bus_get_finish (result, NULL);
3102
3103   if (session_bus && info->app_id)
3104     {
3105       /* FIXME: The g_document_portal_add_documents() function, which is called
3106        * from the g_desktop_app_info_launch_uris_with_dbus() function, still
3107        * uses blocking calls.
3108        */
3109       g_desktop_app_info_launch_uris_with_dbus (info, session_bus,
3110                                                 data->uris, data->context,
3111                                                 cancellable,
3112                                                 launch_uris_with_dbus_cb,
3113                                                 g_steal_pointer (&task));
3114     }
3115   else
3116     {
3117       /* FIXME: The D-Bus message from the notify_desktop_launch() function
3118        * can be still lost even if flush is called later. See:
3119        * https://gitlab.freedesktop.org/dbus/dbus/issues/72
3120        */
3121       g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec,
3122                                                  data->uris, data->context,
3123                                                  _SPAWN_FLAGS_DEFAULT, NULL,
3124                                                  NULL, NULL, NULL, -1, -1, -1,
3125                                                  &error);
3126       if (error != NULL)
3127         {
3128           g_task_return_error (task, g_steal_pointer (&error));
3129           g_object_unref (task);
3130         }
3131       else
3132         g_dbus_connection_flush (session_bus,
3133                                  cancellable,
3134                                  launch_uris_flush_cb,
3135                                  g_steal_pointer (&task));
3136     }
3137
3138   g_clear_object (&session_bus);
3139 }
3140
3141 static void
3142 g_desktop_app_info_launch_uris_async (GAppInfo           *appinfo,
3143                                       GList              *uris,
3144                                       GAppLaunchContext  *context,
3145                                       GCancellable       *cancellable,
3146                                       GAsyncReadyCallback callback,
3147                                       gpointer            user_data)
3148 {
3149   GTask *task;
3150   LaunchUrisData *data;
3151
3152   task = g_task_new (appinfo, cancellable, callback, user_data);
3153   g_task_set_source_tag (task, g_desktop_app_info_launch_uris_async);
3154
3155   data = g_new0 (LaunchUrisData, 1);
3156   data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
3157   data->context = (context != NULL) ? g_object_ref (context) : NULL;
3158   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
3159
3160   g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
3161 }
3162
3163 static gboolean
3164 g_desktop_app_info_launch_uris_finish (GAppInfo     *appinfo,
3165                                        GAsyncResult *result,
3166                                        GError      **error)
3167 {
3168   g_return_val_if_fail (g_task_is_valid (result, appinfo), FALSE);
3169
3170   return g_task_propagate_boolean (G_TASK (result), error);
3171 }
3172
3173 static gboolean
3174 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
3175 {
3176   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3177
3178   return info->exec &&
3179     ((strstr (info->exec, "%u") != NULL) ||
3180      (strstr (info->exec, "%U") != NULL));
3181 }
3182
3183 static gboolean
3184 g_desktop_app_info_supports_files (GAppInfo *appinfo)
3185 {
3186   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3187
3188   return info->exec &&
3189     ((strstr (info->exec, "%f") != NULL) ||
3190      (strstr (info->exec, "%F") != NULL));
3191 }
3192
3193 static gboolean
3194 g_desktop_app_info_launch (GAppInfo           *appinfo,
3195                            GList              *files,
3196                            GAppLaunchContext  *launch_context,
3197                            GError            **error)
3198 {
3199   GList *uris;
3200   char *uri;
3201   gboolean res;
3202
3203   uris = NULL;
3204   while (files)
3205     {
3206       uri = g_file_get_uri (files->data);
3207       uris = g_list_prepend (uris, uri);
3208       files = files->next;
3209     }
3210
3211   uris = g_list_reverse (uris);
3212
3213   res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
3214
3215   g_list_free_full (uris, g_free);
3216
3217   return res;
3218 }
3219
3220 /**
3221  * g_desktop_app_info_launch_uris_as_manager_with_fds:
3222  * @appinfo: a #GDesktopAppInfo
3223  * @uris: (element-type utf8): List of URIs
3224  * @launch_context: (nullable): a #GAppLaunchContext
3225  * @spawn_flags: #GSpawnFlags, used for each process
3226  * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once
3227  *     for each process.
3228  * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
3229  * @pid_callback: (scope call) (nullable): Callback for child processes
3230  * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
3231  * @stdin_fd: file descriptor to use for child's stdin, or -1
3232  * @stdout_fd: file descriptor to use for child's stdout, or -1
3233  * @stderr_fd: file descriptor to use for child's stderr, or -1
3234  * @error: return location for a #GError, or %NULL
3235  *
3236  * Equivalent to g_desktop_app_info_launch_uris_as_manager() but allows
3237  * you to pass in file descriptors for the stdin, stdout and stderr streams
3238  * of the launched process.
3239  *
3240  * If application launching occurs via some non-spawn mechanism (e.g. D-Bus
3241  * activation) then @stdin_fd, @stdout_fd and @stderr_fd are ignored.
3242  *
3243  * Returns: %TRUE on successful launch, %FALSE otherwise.
3244  *
3245  * Since: 2.58
3246  */
3247 gboolean
3248 g_desktop_app_info_launch_uris_as_manager_with_fds (GDesktopAppInfo            *appinfo,
3249                                                     GList                      *uris,
3250                                                     GAppLaunchContext          *launch_context,
3251                                                     GSpawnFlags                 spawn_flags,
3252                                                     GSpawnChildSetupFunc        user_setup,
3253                                                     gpointer                    user_setup_data,
3254                                                     GDesktopAppLaunchCallback   pid_callback,
3255                                                     gpointer                    pid_callback_data,
3256                                                     gint                        stdin_fd,
3257                                                     gint                        stdout_fd,
3258                                                     gint                        stderr_fd,
3259                                                     GError                    **error)
3260 {
3261   return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
3262                                                   uris,
3263                                                   launch_context,
3264                                                   spawn_flags,
3265                                                   user_setup,
3266                                                   user_setup_data,
3267                                                   pid_callback,
3268                                                   pid_callback_data,
3269                                                   stdin_fd,
3270                                                   stdout_fd,
3271                                                   stderr_fd,
3272                                                   error);
3273 }
3274
3275 /**
3276  * g_desktop_app_info_launch_uris_as_manager:
3277  * @appinfo: a #GDesktopAppInfo
3278  * @uris: (element-type utf8): List of URIs
3279  * @launch_context: (nullable): a #GAppLaunchContext
3280  * @spawn_flags: #GSpawnFlags, used for each process
3281  * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once
3282  *     for each process.
3283  * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
3284  * @pid_callback: (scope call) (nullable): Callback for child processes
3285  * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
3286  * @error: return location for a #GError, or %NULL
3287  *
3288  * This function performs the equivalent of g_app_info_launch_uris(),
3289  * but is intended primarily for operating system components that
3290  * launch applications.  Ordinary applications should use
3291  * g_app_info_launch_uris().
3292  *
3293  * If the application is launched via GSpawn, then @spawn_flags, @user_setup
3294  * and @user_setup_data are used for the call to g_spawn_async().
3295  * Additionally, @pid_callback (with @pid_callback_data) will be called to
3296  * inform about the PID of the created process. See g_spawn_async_with_pipes()
3297  * for information on certain parameter conditions that can enable an
3298  * optimized posix_spawn() codepath to be used.
3299  *
3300  * If application launching occurs via some other mechanism (eg: D-Bus
3301  * activation) then @spawn_flags, @user_setup, @user_setup_data,
3302  * @pid_callback and @pid_callback_data are ignored.
3303  *
3304  * Returns: %TRUE on successful launch, %FALSE otherwise.
3305  */
3306 gboolean
3307 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo            *appinfo,
3308                                            GList                      *uris,
3309                                            GAppLaunchContext          *launch_context,
3310                                            GSpawnFlags                 spawn_flags,
3311                                            GSpawnChildSetupFunc        user_setup,
3312                                            gpointer                    user_setup_data,
3313                                            GDesktopAppLaunchCallback   pid_callback,
3314                                            gpointer                    pid_callback_data,
3315                                            GError                    **error)
3316 {
3317   return g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
3318                                                              uris,
3319                                                              launch_context,
3320                                                              spawn_flags,
3321                                                              user_setup,
3322                                                              user_setup_data,
3323                                                              pid_callback,
3324                                                              pid_callback_data,
3325                                                              -1, -1, -1,
3326                                                              error);
3327 }
3328
3329 /* OnlyShowIn API support {{{2 */
3330
3331 /**
3332  * g_desktop_app_info_set_desktop_env:
3333  * @desktop_env: a string specifying what desktop this is
3334  *
3335  * Sets the name of the desktop that the application is running in.
3336  * This is used by g_app_info_should_show() and
3337  * g_desktop_app_info_get_show_in() to evaluate the
3338  * `OnlyShowIn` and `NotShowIn`
3339  * desktop entry fields.
3340  *
3341  * Should be called only once; subsequent calls are ignored.
3342  *
3343  * Deprecated:2.42:do not use this API.  Since 2.42 the value of the
3344  * `XDG_CURRENT_DESKTOP` environment variable will be used.
3345  */
3346 void
3347 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
3348 {
3349   get_current_desktops (desktop_env);
3350 }
3351
3352 static gboolean
3353 g_desktop_app_info_should_show (GAppInfo *appinfo)
3354 {
3355   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3356
3357   if (info->nodisplay)
3358     return FALSE;
3359
3360   return g_desktop_app_info_get_show_in (info, NULL);
3361 }
3362
3363 /* mime types/default apps support {{{2 */
3364
3365 typedef enum {
3366   CONF_DIR,
3367   APP_DIR,
3368   MIMETYPE_DIR
3369 } DirType;
3370
3371 static char *
3372 ensure_dir (DirType   type,
3373             GError  **error)
3374 {
3375   char *path, *display_name;
3376   int errsv;
3377
3378   switch (type)
3379     {
3380     case CONF_DIR:
3381       path = g_build_filename (g_get_user_config_dir (), NULL);
3382       break;
3383
3384     case APP_DIR:
3385       path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
3386       break;
3387
3388     case MIMETYPE_DIR:
3389       path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
3390       break;
3391
3392     default:
3393       g_assert_not_reached ();
3394     }
3395
3396   g_debug ("%s: Ensuring %s", G_STRFUNC, path);
3397
3398   errno = 0;
3399   if (g_mkdir_with_parents (path, 0700) == 0)
3400     return path;
3401
3402   errsv = errno;
3403   display_name = g_filename_display_name (path);
3404   if (type == APP_DIR)
3405     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
3406                  _("Can’t create user application configuration folder %s: %s"),
3407                  display_name, g_strerror (errsv));
3408   else
3409     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
3410                  _("Can’t create user MIME configuration folder %s: %s"),
3411                  display_name, g_strerror (errsv));
3412
3413   g_free (display_name);
3414   g_free (path);
3415
3416   return NULL;
3417 }
3418
3419 static gboolean
3420 update_mimeapps_list (const char  *desktop_id,
3421                       const char  *content_type,
3422                       UpdateMimeFlags flags,
3423                       GError     **error)
3424 {
3425   char *dirname, *filename, *string;
3426   GKeyFile *key_file;
3427   gboolean load_succeeded, res;
3428   char **old_list, **list;
3429   gsize length, data_size;
3430   char *data;
3431   int i, j, k;
3432   char **content_types;
3433
3434   /* Don't add both at start and end */
3435   g_assert (!((flags & UPDATE_MIME_SET_DEFAULT) &&
3436               (flags & UPDATE_MIME_SET_NON_DEFAULT)));
3437
3438   dirname = ensure_dir (CONF_DIR, error);
3439   if (!dirname)
3440     return FALSE;
3441
3442   filename = g_build_filename (dirname, "mimeapps.list", NULL);
3443   g_free (dirname);
3444
3445   key_file = g_key_file_new ();
3446   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
3447   if (!load_succeeded ||
3448       (!g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP) &&
3449        !g_key_file_has_group (key_file, REMOVED_ASSOCIATIONS_GROUP) &&
3450        !g_key_file_has_group (key_file, DEFAULT_APPLICATIONS_GROUP)))
3451     {
3452       g_key_file_free (key_file);
3453       key_file = g_key_file_new ();
3454     }
3455
3456   if (content_type)
3457     {
3458       content_types = g_new (char *, 2);
3459       content_types[0] = g_strdup (content_type);
3460       content_types[1] = NULL;
3461     }
3462   else
3463     {
3464       content_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, NULL, NULL);
3465     }
3466
3467   for (k = 0; content_types && content_types[k]; k++)
3468     {
3469       /* set as default, if requested so */
3470       string = g_key_file_get_string (key_file,
3471                                       DEFAULT_APPLICATIONS_GROUP,
3472                                       content_types[k],
3473                                       NULL);
3474
3475       if (g_strcmp0 (string, desktop_id) != 0 &&
3476           (flags & UPDATE_MIME_SET_DEFAULT))
3477         {
3478           g_free (string);
3479           string = g_strdup (desktop_id);
3480
3481           /* add in the non-default list too, if it's not already there */
3482           flags |= UPDATE_MIME_SET_NON_DEFAULT;
3483         }
3484
3485       if (string == NULL || desktop_id == NULL)
3486         g_key_file_remove_key (key_file,
3487                                DEFAULT_APPLICATIONS_GROUP,
3488                                content_types[k],
3489                                NULL);
3490       else
3491         g_key_file_set_string (key_file,
3492                                DEFAULT_APPLICATIONS_GROUP,
3493                                content_types[k],
3494                                string);
3495
3496       g_free (string);
3497     }
3498
3499   if (content_type)
3500     {
3501       /* reuse the list from above */
3502     }
3503   else
3504     {
3505       g_strfreev (content_types);
3506       content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
3507     }
3508
3509   for (k = 0; content_types && content_types[k]; k++)
3510     {
3511       /* Add to the right place in the list */
3512
3513       length = 0;
3514       old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
3515                                              content_types[k], &length, NULL);
3516
3517       list = g_new (char *, 1 + length + 1);
3518
3519       i = 0;
3520
3521       /* if we're adding a last-used hint, just put the application in front of the list */
3522       if (flags & UPDATE_MIME_SET_LAST_USED)
3523         {
3524           /* avoid adding this again as non-default later */
3525           if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3526             flags ^= UPDATE_MIME_SET_NON_DEFAULT;
3527
3528           list[i++] = g_strdup (desktop_id);
3529         }
3530
3531       if (old_list)
3532         {
3533           for (j = 0; old_list[j] != NULL; j++)
3534             {
3535               if (g_strcmp0 (old_list[j], desktop_id) != 0)
3536                 {
3537                   /* rewrite other entries if they're different from the new one */
3538                   list[i++] = g_strdup (old_list[j]);
3539                 }
3540               else if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3541                 {
3542                   /* we encountered an old entry which is equal to the one we're adding as non-default,
3543                    * don't change its position in the list.
3544                    */
3545                   flags ^= UPDATE_MIME_SET_NON_DEFAULT;
3546                   list[i++] = g_strdup (old_list[j]);
3547                 }
3548             }
3549         }
3550
3551       /* add it at the end of the list */
3552       if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3553         list[i++] = g_strdup (desktop_id);
3554
3555       list[i] = NULL;
3556
3557       g_strfreev (old_list);
3558
3559       if (list[0] == NULL || desktop_id == NULL)
3560         g_key_file_remove_key (key_file,
3561                                ADDED_ASSOCIATIONS_GROUP,
3562                                content_types[k],
3563                                NULL);
3564       else
3565         g_key_file_set_string_list (key_file,
3566                                     ADDED_ASSOCIATIONS_GROUP,
3567                                     content_types[k],
3568                                     (const char * const *)list, i);
3569
3570       g_strfreev (list);
3571     }
3572
3573   if (content_type)
3574     {
3575       /* reuse the list from above */
3576     }
3577   else
3578     {
3579       g_strfreev (content_types);
3580       content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
3581     }
3582
3583   for (k = 0; content_types && content_types[k]; k++)
3584     {
3585       /* Remove from removed associations group (unless remove) */
3586
3587       length = 0;
3588       old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
3589                                              content_types[k], &length, NULL);
3590
3591       list = g_new (char *, 1 + length + 1);
3592
3593       i = 0;
3594       if (flags & UPDATE_MIME_REMOVE)
3595         list[i++] = g_strdup (desktop_id);
3596       if (old_list)
3597         {
3598           for (j = 0; old_list[j] != NULL; j++)
3599             {
3600               if (g_strcmp0 (old_list[j], desktop_id) != 0)
3601                 list[i++] = g_strdup (old_list[j]);
3602             }
3603         }
3604       list[i] = NULL;
3605
3606       g_strfreev (old_list);
3607
3608       if (list[0] == NULL || desktop_id == NULL)
3609         g_key_file_remove_key (key_file,
3610                                REMOVED_ASSOCIATIONS_GROUP,
3611                                content_types[k],
3612                                NULL);
3613       else
3614         g_key_file_set_string_list (key_file,
3615                                     REMOVED_ASSOCIATIONS_GROUP,
3616                                     content_types[k],
3617                                     (const char * const *)list, i);
3618
3619       g_strfreev (list);
3620     }
3621
3622   g_strfreev (content_types);
3623
3624   data = g_key_file_to_data (key_file, &data_size, error);
3625   g_key_file_free (key_file);
3626
3627   res = g_file_set_contents (filename, data, data_size, error);
3628
3629   desktop_file_dirs_invalidate_user_config ();
3630
3631   g_free (filename);
3632   g_free (data);
3633
3634   return res;
3635 }
3636
3637 static gboolean
3638 g_desktop_app_info_set_as_last_used_for_type (GAppInfo    *appinfo,
3639                                               const char  *content_type,
3640                                               GError     **error)
3641 {
3642   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3643
3644   if (!g_desktop_app_info_ensure_saved (info, error))
3645     return FALSE;
3646
3647   if (!info->desktop_id)
3648     {
3649       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3650                            _("Application information lacks an identifier"));
3651       return FALSE;
3652     }
3653
3654   /* both add support for the content type and set as last used */
3655   return update_mimeapps_list (info->desktop_id, content_type,
3656                                UPDATE_MIME_SET_NON_DEFAULT |
3657                                UPDATE_MIME_SET_LAST_USED,
3658                                error);
3659 }
3660
3661 static gboolean
3662 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
3663                                             const char  *content_type,
3664                                             GError     **error)
3665 {
3666   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3667
3668   if (!g_desktop_app_info_ensure_saved (info, error))
3669     return FALSE;
3670
3671   if (!info->desktop_id)
3672     {
3673       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3674                            _("Application information lacks an identifier"));
3675       return FALSE;
3676     }
3677
3678   return update_mimeapps_list (info->desktop_id, content_type,
3679                                UPDATE_MIME_SET_DEFAULT,
3680                                error);
3681 }
3682
3683 static void
3684 update_program_done (GPid     pid,
3685                      gint     status,
3686                      gpointer data)
3687 {
3688   /* Did the application exit correctly */
3689   if (g_spawn_check_exit_status (status, NULL))
3690     {
3691       /* Here we could clean out any caches in use */
3692     }
3693 }
3694
3695 static void
3696 run_update_command (char *command,
3697                     char *subdir)
3698 {
3699         char *argv[3] = {
3700                 NULL,
3701                 NULL,
3702                 NULL,
3703         };
3704         GPid pid = 0;
3705         GError *error = NULL;
3706
3707         argv[0] = command;
3708         argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
3709
3710         if (g_spawn_async ("/", argv,
3711                            NULL,       /* envp */
3712                            G_SPAWN_SEARCH_PATH |
3713                            G_SPAWN_STDOUT_TO_DEV_NULL |
3714                            G_SPAWN_STDERR_TO_DEV_NULL |
3715                            G_SPAWN_DO_NOT_REAP_CHILD,
3716                            NULL, NULL, /* No setup function */
3717                            &pid,
3718                            &error))
3719           g_child_watch_add (pid, update_program_done, NULL);
3720         else
3721           {
3722             /* If we get an error at this point, it's quite likely the user doesn't
3723              * have an installed copy of either 'update-mime-database' or
3724              * 'update-desktop-database'.  I don't think we want to popup an error
3725              * dialog at this point, so we just do a g_warning to give the user a
3726              * chance of debugging it.
3727              */
3728             g_warning ("%s", error->message);
3729             g_error_free (error);
3730           }
3731
3732         g_free (argv[1]);
3733 }
3734
3735 static gboolean
3736 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
3737                                                  const char  *extension,
3738                                                  GError     **error)
3739 {
3740   char *filename, *basename, *mimetype;
3741   char *dirname;
3742   gboolean res;
3743
3744   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
3745     return FALSE;
3746
3747   dirname = ensure_dir (MIMETYPE_DIR, error);
3748   if (!dirname)
3749     return FALSE;
3750
3751   basename = g_strdup_printf ("user-extension-%s.xml", extension);
3752   filename = g_build_filename (dirname, basename, NULL);
3753   g_free (basename);
3754   g_free (dirname);
3755
3756   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
3757
3758   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
3759     {
3760       char *contents;
3761
3762       contents =
3763         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3764                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3765                          " <mime-type type=\"%s\">\n"
3766                          "  <comment>%s document</comment>\n"
3767                          "  <glob pattern=\"*.%s\"/>\n"
3768                          " </mime-type>\n"
3769                          "</mime-info>\n", mimetype, extension, extension);
3770
3771       g_file_set_contents (filename, contents, -1, NULL);
3772       g_free (contents);
3773
3774       run_update_command ("update-mime-database", "mime");
3775     }
3776   g_free (filename);
3777
3778   res = g_desktop_app_info_set_as_default_for_type (appinfo,
3779                                                     mimetype,
3780                                                     error);
3781
3782   g_free (mimetype);
3783
3784   return res;
3785 }
3786
3787 static gboolean
3788 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
3789                                       const char  *content_type,
3790                                       GError     **error)
3791 {
3792   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3793
3794   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
3795     return FALSE;
3796
3797   return update_mimeapps_list (info->desktop_id, content_type,
3798                                UPDATE_MIME_SET_NON_DEFAULT,
3799                                error);
3800 }
3801
3802 static gboolean
3803 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
3804 {
3805   return TRUE;
3806 }
3807
3808 static gboolean
3809 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
3810                                          const char  *content_type,
3811                                          GError     **error)
3812 {
3813   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3814
3815   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
3816     return FALSE;
3817
3818   return update_mimeapps_list (info->desktop_id, content_type,
3819                                UPDATE_MIME_REMOVE,
3820                                error);
3821 }
3822
3823 static const char **
3824 g_desktop_app_info_get_supported_types (GAppInfo *appinfo)
3825 {
3826   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3827
3828   return (const char**) info->mime_types;
3829 }
3830
3831 /* Saving and deleting {{{2 */
3832
3833 static gboolean
3834 g_desktop_app_info_ensure_saved (GDesktopAppInfo  *info,
3835                                  GError          **error)
3836 {
3837   GKeyFile *key_file;
3838   char *dirname;
3839   char *filename;
3840   char *data, *desktop_id;
3841   gsize data_size;
3842   int fd;
3843   gboolean res;
3844
3845   if (info->filename != NULL)
3846     return TRUE;
3847
3848   /* This is only used for object created with
3849    * g_app_info_create_from_commandline. All other
3850    * object should have a filename
3851    */
3852
3853   dirname = ensure_dir (APP_DIR, error);
3854   if (!dirname)
3855     return FALSE;
3856
3857   key_file = g_key_file_new ();
3858
3859   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3860                          "Encoding", "UTF-8");
3861   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3862                          G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
3863   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3864                          G_KEY_FILE_DESKTOP_KEY_TYPE,
3865                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
3866   if (info->terminal)
3867     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3868                             G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
3869   if (info->nodisplay)
3870     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3871                             G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
3872
3873   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3874                          G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
3875
3876   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3877                          G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
3878
3879   if (info->generic_name != NULL)
3880     g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3881                            GENERIC_NAME_KEY, info->generic_name);
3882
3883   if (info->fullname != NULL)
3884     g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3885                            FULL_NAME_KEY, info->fullname);
3886
3887   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3888                          G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
3889
3890   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3891                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
3892
3893   data = g_key_file_to_data (key_file, &data_size, NULL);
3894   g_key_file_free (key_file);
3895
3896   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
3897   filename = g_build_filename (dirname, desktop_id, NULL);
3898   g_free (desktop_id);
3899   g_free (dirname);
3900
3901   fd = g_mkstemp (filename);
3902   if (fd == -1)
3903     {
3904       char *display_name;
3905
3906       display_name = g_filename_display_name (filename);
3907       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3908                    _("Can’t create user desktop file %s"), display_name);
3909       g_free (display_name);
3910       g_free (filename);
3911       g_free (data);
3912       return FALSE;
3913     }
3914
3915   desktop_id = g_path_get_basename (filename);
3916
3917   /* FIXME - actually handle error */
3918   (void) g_close (fd, NULL);
3919
3920   res = g_file_set_contents (filename, data, data_size, error);
3921   g_free (data);
3922   if (!res)
3923     {
3924       g_free (desktop_id);
3925       g_free (filename);
3926       return FALSE;
3927     }
3928
3929   info->filename = filename;
3930   info->desktop_id = desktop_id;
3931
3932   run_update_command ("update-desktop-database", "applications");
3933
3934   /* We just dropped a file in the user's desktop file directory.  Save
3935    * the monitor the bother of having to notice it and invalidate
3936    * immediately.
3937    *
3938    * This means that calls directly following this will be able to see
3939    * the results immediately.
3940    */
3941   desktop_file_dirs_invalidate_user_data ();
3942
3943   return TRUE;
3944 }
3945
3946 static gboolean
3947 g_desktop_app_info_can_delete (GAppInfo *appinfo)
3948 {
3949   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3950
3951   if (info->filename)
3952     {
3953       if (strstr (info->filename, "/userapp-"))
3954         return g_access (info->filename, W_OK) == 0;
3955     }
3956
3957   return FALSE;
3958 }
3959
3960 static gboolean
3961 g_desktop_app_info_delete (GAppInfo *appinfo)
3962 {
3963   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3964
3965   if (info->filename)
3966     {
3967       if (g_remove (info->filename) == 0)
3968         {
3969           update_mimeapps_list (info->desktop_id, NULL,
3970                                 UPDATE_MIME_NONE,
3971                                 NULL);
3972
3973           g_free (info->filename);
3974           info->filename = NULL;
3975           g_free (info->desktop_id);
3976           info->desktop_id = NULL;
3977
3978           return TRUE;
3979         }
3980     }
3981
3982   return FALSE;
3983 }
3984
3985 /* Create for commandline {{{2 */
3986 /**
3987  * g_app_info_create_from_commandline:
3988  * @commandline: (type filename): the commandline to use
3989  * @application_name: (nullable): the application name, or %NULL to use @commandline
3990  * @flags: flags that can specify details of the created #GAppInfo
3991  * @error: a #GError location to store the error occurring, %NULL to ignore.
3992  *
3993  * Creates a new #GAppInfo from the given information.
3994  *
3995  * Note that for @commandline, the quoting rules of the Exec key of the
3996  * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3997  * are applied. For example, if the @commandline contains
3998  * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3999  * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
4000  *
4001  * Returns: (transfer full): new #GAppInfo for given command.
4002  **/
4003 GAppInfo *
4004 g_app_info_create_from_commandline (const char           *commandline,
4005                                     const char           *application_name,
4006                                     GAppInfoCreateFlags   flags,
4007                                     GError              **error)
4008 {
4009   char **split;
4010   char *basename;
4011   GDesktopAppInfo *info;
4012
4013   g_return_val_if_fail (commandline, NULL);
4014
4015   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
4016
4017   info->filename = NULL;
4018   info->desktop_id = NULL;
4019
4020   info->terminal = (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) != 0;
4021   info->startup_notify = (flags & G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION) != 0;
4022   info->hidden = FALSE;
4023   if ((flags & G_APP_INFO_CREATE_SUPPORTS_URIS) != 0)
4024     info->exec = g_strconcat (commandline, " %u", NULL);
4025   else
4026     info->exec = g_strconcat (commandline, " %f", NULL);
4027   info->nodisplay = TRUE;
4028   info->binary = binary_from_exec (info->exec);
4029
4030   if (application_name)
4031     info->name = g_strdup (application_name);
4032   else
4033     {
4034       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
4035       split = g_strsplit (commandline, " ", 2);
4036       basename = split[0] ? g_path_get_basename (split[0]) : NULL;
4037       g_strfreev (split);
4038       info->name = basename;
4039       if (info->name == NULL)
4040         info->name = g_strdup ("custom");
4041     }
4042   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
4043
4044   return G_APP_INFO (info);
4045 }
4046
4047 /* GAppInfo interface init */
4048
4049 static void
4050 g_desktop_app_info_iface_init (GAppInfoIface *iface)
4051 {
4052   iface->dup = g_desktop_app_info_dup;
4053   iface->equal = g_desktop_app_info_equal;
4054   iface->get_id = g_desktop_app_info_get_id;
4055   iface->get_name = g_desktop_app_info_get_name;
4056   iface->get_description = g_desktop_app_info_get_description;
4057   iface->get_executable = g_desktop_app_info_get_executable;
4058   iface->get_icon = g_desktop_app_info_get_icon;
4059   iface->launch = g_desktop_app_info_launch;
4060   iface->supports_uris = g_desktop_app_info_supports_uris;
4061   iface->supports_files = g_desktop_app_info_supports_files;
4062   iface->launch_uris = g_desktop_app_info_launch_uris;
4063   iface->launch_uris_async = g_desktop_app_info_launch_uris_async;
4064   iface->launch_uris_finish = g_desktop_app_info_launch_uris_finish;
4065   iface->should_show = g_desktop_app_info_should_show;
4066   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
4067   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
4068   iface->add_supports_type = g_desktop_app_info_add_supports_type;
4069   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
4070   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
4071   iface->can_delete = g_desktop_app_info_can_delete;
4072   iface->do_delete = g_desktop_app_info_delete;
4073   iface->get_commandline = g_desktop_app_info_get_commandline;
4074   iface->get_display_name = g_desktop_app_info_get_display_name;
4075   iface->set_as_last_used_for_type = g_desktop_app_info_set_as_last_used_for_type;
4076   iface->get_supported_types = g_desktop_app_info_get_supported_types;
4077 }
4078
4079 /* Recommended applications {{{2 */
4080
4081 /* Converts content_type into a list of itself with all of its parent
4082  * types (if include_fallback is enabled) or just returns a single-item
4083  * list with the unaliased content type.
4084  */
4085 static gchar **
4086 get_list_of_mimetypes (const gchar *content_type,
4087                        gboolean     include_fallback)
4088 {
4089   gchar *unaliased;
4090   GPtrArray *array;
4091
4092   array = g_ptr_array_new ();
4093   unaliased = _g_unix_content_type_unalias (content_type);
4094   g_ptr_array_add (array, unaliased);
4095
4096   if (include_fallback)
4097     {
4098       gint i;
4099
4100       /* Iterate the array as we grow it, until we have nothing more to add */
4101       for (i = 0; i < array->len; i++)
4102         {
4103           gchar **parents = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
4104           gint j;
4105
4106           for (j = 0; parents[j]; j++)
4107             /* Don't add duplicates */
4108             if (!array_contains (array, parents[j]))
4109               g_ptr_array_add (array, parents[j]);
4110             else
4111               g_free (parents[j]);
4112
4113           /* We already stole or freed each element.  Free the container. */
4114           g_free (parents);
4115         }
4116     }
4117
4118   g_ptr_array_add (array, NULL);
4119
4120   return (gchar **) g_ptr_array_free (array, FALSE);
4121 }
4122
4123 static gchar **
4124 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar *content_type,
4125                                                      gboolean     include_fallback)
4126 {
4127   GPtrArray *hits, *blacklist;
4128   gchar **types;
4129   gint i, j;
4130
4131   hits = g_ptr_array_new ();
4132   blacklist = g_ptr_array_new ();
4133
4134   types = get_list_of_mimetypes (content_type, include_fallback);
4135
4136   desktop_file_dirs_lock ();
4137
4138   for (i = 0; types[i]; i++)
4139     for (j = 0; j < desktop_file_dirs->len; j++)
4140       desktop_file_dir_mime_lookup (g_ptr_array_index (desktop_file_dirs, j), types[i], hits, blacklist);
4141
4142   /* We will keep the hits past unlocking, so we must dup them */
4143   for (i = 0; i < hits->len; i++)
4144     hits->pdata[i] = g_strdup (hits->pdata[i]);
4145
4146   desktop_file_dirs_unlock ();
4147
4148   g_ptr_array_add (hits, NULL);
4149
4150   g_ptr_array_free (blacklist, TRUE);
4151   g_strfreev (types);
4152
4153   return (gchar **) g_ptr_array_free (hits, FALSE);
4154 }
4155
4156 /**
4157  * g_app_info_get_recommended_for_type:
4158  * @content_type: the content type to find a #GAppInfo for
4159  *
4160  * Gets a list of recommended #GAppInfos for a given content type, i.e.
4161  * those applications which claim to support the given content type exactly,
4162  * and not by MIME type subclassing.
4163  * Note that the first application of the list is the last used one, i.e.
4164  * the last one for which g_app_info_set_as_last_used_for_type() has been
4165  * called.
4166  *
4167  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
4168  *     for given @content_type or %NULL on error.
4169  *
4170  * Since: 2.28
4171  **/
4172 GList *
4173 g_app_info_get_recommended_for_type (const gchar *content_type)
4174 {
4175   gchar **desktop_ids;
4176   GList *infos;
4177   gint i;
4178
4179   g_return_val_if_fail (content_type != NULL, NULL);
4180
4181   desktop_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, FALSE);
4182
4183   infos = NULL;
4184   for (i = 0; desktop_ids[i]; i++)
4185     {
4186       GDesktopAppInfo *info;
4187
4188       info = g_desktop_app_info_new (desktop_ids[i]);
4189       if (info)
4190         infos = g_list_prepend (infos, info);
4191     }
4192
4193   g_strfreev (desktop_ids);
4194
4195   return g_list_reverse (infos);
4196 }
4197
4198 /**
4199  * g_app_info_get_fallback_for_type:
4200  * @content_type: the content type to find a #GAppInfo for
4201  *
4202  * Gets a list of fallback #GAppInfos for a given content type, i.e.
4203  * those applications which claim to support the given content type
4204  * by MIME type subclassing and not directly.
4205  *
4206  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
4207  *     for given @content_type or %NULL on error.
4208  *
4209  * Since: 2.28
4210  **/
4211 GList *
4212 g_app_info_get_fallback_for_type (const gchar *content_type)
4213 {
4214   gchar **recommended_ids;
4215   gchar **all_ids;
4216   GList *infos;
4217   gint i;
4218
4219   g_return_val_if_fail (content_type != NULL, NULL);
4220
4221   recommended_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, FALSE);
4222   all_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, TRUE);
4223
4224   infos = NULL;
4225   for (i = 0; all_ids[i]; i++)
4226     {
4227       GDesktopAppInfo *info;
4228       gint j;
4229
4230       /* Don't return the ones on the recommended list */
4231       for (j = 0; recommended_ids[j]; j++)
4232         if (g_str_equal (all_ids[i], recommended_ids[j]))
4233           break;
4234
4235       if (recommended_ids[j])
4236         continue;
4237
4238       info = g_desktop_app_info_new (all_ids[i]);
4239
4240       if (info)
4241         infos = g_list_prepend (infos, info);
4242     }
4243
4244   g_strfreev (recommended_ids);
4245   g_strfreev (all_ids);
4246
4247   return g_list_reverse (infos);
4248 }
4249
4250 /**
4251  * g_app_info_get_all_for_type:
4252  * @content_type: the content type to find a #GAppInfo for
4253  *
4254  * Gets a list of all #GAppInfos for a given content type,
4255  * including the recommended and fallback #GAppInfos. See
4256  * g_app_info_get_recommended_for_type() and
4257  * g_app_info_get_fallback_for_type().
4258  *
4259  * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
4260  *     for given @content_type or %NULL on error.
4261  **/
4262 GList *
4263 g_app_info_get_all_for_type (const char *content_type)
4264 {
4265   gchar **desktop_ids;
4266   GList *infos;
4267   gint i;
4268
4269   g_return_val_if_fail (content_type != NULL, NULL);
4270
4271   desktop_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, TRUE);
4272
4273   infos = NULL;
4274   for (i = 0; desktop_ids[i]; i++)
4275     {
4276       GDesktopAppInfo *info;
4277
4278       info = g_desktop_app_info_new (desktop_ids[i]);
4279       if (info)
4280         infos = g_list_prepend (infos, info);
4281     }
4282
4283   g_strfreev (desktop_ids);
4284
4285   return g_list_reverse (infos);
4286 }
4287
4288 /**
4289  * g_app_info_reset_type_associations:
4290  * @content_type: a content type
4291  *
4292  * Removes all changes to the type associations done by
4293  * g_app_info_set_as_default_for_type(),
4294  * g_app_info_set_as_default_for_extension(),
4295  * g_app_info_add_supports_type() or
4296  * g_app_info_remove_supports_type().
4297  *
4298  * Since: 2.20
4299  */
4300 void
4301 g_app_info_reset_type_associations (const char *content_type)
4302 {
4303   update_mimeapps_list (NULL, content_type,
4304                         UPDATE_MIME_NONE,
4305                         NULL);
4306 }
4307
4308 /**
4309  * g_app_info_get_default_for_type:
4310  * @content_type: the content type to find a #GAppInfo for
4311  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
4312  *     support URIs
4313  *
4314  * Gets the default #GAppInfo for a given content type.
4315  *
4316  * Returns: (transfer full): #GAppInfo for given @content_type or
4317  *     %NULL on error.
4318  */
4319 GAppInfo *
4320 g_app_info_get_default_for_type (const char *content_type,
4321                                  gboolean    must_support_uris)
4322 {
4323   GPtrArray *blacklist;
4324   GPtrArray *results;
4325   GAppInfo *info;
4326   gchar **types;
4327   gint i, j, k;
4328
4329   g_return_val_if_fail (content_type != NULL, NULL);
4330
4331   types = get_list_of_mimetypes (content_type, TRUE);
4332
4333   blacklist = g_ptr_array_new ();
4334   results = g_ptr_array_new ();
4335   info = NULL;
4336
4337   desktop_file_dirs_lock ();
4338
4339   for (i = 0; types[i]; i++)
4340     {
4341       /* Collect all the default apps for this type */
4342       for (j = 0; j < desktop_file_dirs->len; j++)
4343         desktop_file_dir_default_lookup (g_ptr_array_index (desktop_file_dirs, j), types[i], results);
4344
4345       /* Consider the associations as well... */
4346       for (j = 0; j < desktop_file_dirs->len; j++)
4347         desktop_file_dir_mime_lookup (g_ptr_array_index (desktop_file_dirs, j), types[i], results, blacklist);
4348
4349       /* (If any), see if one of those apps is installed... */
4350       for (j = 0; j < results->len; j++)
4351         {
4352           const gchar *desktop_id = g_ptr_array_index (results, j);
4353
4354           for (k = 0; k < desktop_file_dirs->len; k++)
4355             {
4356               info = (GAppInfo *) desktop_file_dir_get_app (g_ptr_array_index (desktop_file_dirs, k), desktop_id);
4357
4358               if (info)
4359                 {
4360                   if (!must_support_uris || g_app_info_supports_uris (info))
4361                     goto out;
4362
4363                   g_clear_object (&info);
4364                 }
4365             }
4366         }
4367
4368       /* Reset the list, ready to try again with the next (parent)
4369        * mimetype, but keep the blacklist in place.
4370        */
4371       g_ptr_array_set_size (results, 0);
4372     }
4373
4374 out:
4375   desktop_file_dirs_unlock ();
4376
4377   g_ptr_array_unref (blacklist);
4378   g_ptr_array_unref (results);
4379   g_strfreev (types);
4380
4381   return info;
4382 }
4383
4384 /**
4385  * g_app_info_get_default_for_uri_scheme:
4386  * @uri_scheme: a string containing a URI scheme.
4387  *
4388  * Gets the default application for handling URIs with
4389  * the given URI scheme. A URI scheme is the initial part
4390  * of the URI, up to but not including the ':', e.g. "http",
4391  * "ftp" or "sip".
4392  *
4393  * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4394  */
4395 GAppInfo *
4396 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4397 {
4398   GAppInfo *app_info;
4399   char *content_type, *scheme_down;
4400
4401   scheme_down = g_ascii_strdown (uri_scheme, -1);
4402   content_type = g_strdup_printf ("x-scheme-handler/%s", scheme_down);
4403   g_free (scheme_down);
4404   app_info = g_app_info_get_default_for_type (content_type, FALSE);
4405   g_free (content_type);
4406
4407   return app_info;
4408 }
4409
4410 /* "Get all" API {{{2 */
4411
4412 /**
4413  * g_desktop_app_info_get_implementations:
4414  * @interface: the name of the interface
4415  *
4416  * Gets all applications that implement @interface.
4417  *
4418  * An application implements an interface if that interface is listed in
4419  * the Implements= line of the desktop file of the application.
4420  *
4421  * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo
4422  * objects.
4423  *
4424  * Since: 2.42
4425  **/
4426 GList *
4427 g_desktop_app_info_get_implementations (const gchar *interface)
4428 {
4429   GList *result = NULL;
4430   GList **ptr;
4431   gint i;
4432
4433   desktop_file_dirs_lock ();
4434
4435   for (i = 0; i < desktop_file_dirs->len; i++)
4436     desktop_file_dir_get_implementations (g_ptr_array_index (desktop_file_dirs, i), &result, interface);
4437
4438   desktop_file_dirs_unlock ();
4439
4440   ptr = &result;
4441   while (*ptr)
4442     {
4443       gchar *name = (*ptr)->data;
4444       GDesktopAppInfo *app;
4445
4446       app = g_desktop_app_info_new (name);
4447       g_free (name);
4448
4449       if (app)
4450         {
4451           (*ptr)->data = app;
4452           ptr = &(*ptr)->next;
4453         }
4454       else
4455         *ptr = g_list_delete_link (*ptr, *ptr);
4456     }
4457
4458   return result;
4459 }
4460
4461 /**
4462  * g_desktop_app_info_search:
4463  * @search_string: the search string to use
4464  *
4465  * Searches desktop files for ones that match @search_string.
4466  *
4467  * The return value is an array of strvs.  Each strv contains a list of
4468  * applications that matched @search_string with an equal score.  The
4469  * outer list is sorted by score so that the first strv contains the
4470  * best-matching applications, and so on.
4471  * The algorithm for determining matches is undefined and may change at
4472  * any time.
4473  *
4474  * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4475  *   list of strvs.  Free each item with g_strfreev() and free the outer
4476  *   list with g_free().
4477  */
4478 gchar ***
4479 g_desktop_app_info_search (const gchar *search_string)
4480 {
4481   gchar **search_tokens;
4482   gint last_category = -1;
4483   gchar ***results;
4484   gint n_categories = 0;
4485   gint start_of_category;
4486   gint i, j;
4487
4488   search_tokens = g_str_tokenize_and_fold (search_string, NULL, NULL);
4489
4490   desktop_file_dirs_lock ();
4491
4492   reset_total_search_results ();
4493
4494   for (i = 0; i < desktop_file_dirs->len; i++)
4495     {
4496       for (j = 0; search_tokens[j]; j++)
4497         {
4498           desktop_file_dir_search (g_ptr_array_index (desktop_file_dirs, i), search_tokens[j]);
4499           merge_token_results (j == 0);
4500         }
4501       merge_directory_results ();
4502     }
4503
4504   sort_total_search_results ();
4505
4506   /* Count the total number of unique categories */
4507   for (i = 0; i < static_total_results_size; i++)
4508     if (static_total_results[i].category != last_category)
4509       {
4510         last_category = static_total_results[i].category;
4511         n_categories++;
4512       }
4513
4514   results = g_new (gchar **, n_categories + 1);
4515
4516   /* Start loading into the results list */
4517   start_of_category = 0;
4518   for (i = 0; i < n_categories; i++)
4519     {
4520       gint n_items_in_category = 0;
4521       gint this_category;
4522       gint j;
4523
4524       this_category = static_total_results[start_of_category].category;
4525
4526       while (start_of_category + n_items_in_category < static_total_results_size &&
4527              static_total_results[start_of_category + n_items_in_category].category == this_category)
4528         n_items_in_category++;
4529
4530       results[i] = g_new (gchar *, n_items_in_category + 1);
4531       for (j = 0; j < n_items_in_category; j++)
4532         results[i][j] = g_strdup (static_total_results[start_of_category + j].app_name);
4533       results[i][j] = NULL;
4534
4535       start_of_category += n_items_in_category;
4536     }
4537   results[i] = NULL;
4538
4539   desktop_file_dirs_unlock ();
4540
4541   g_strfreev (search_tokens);
4542
4543   return results;
4544 }
4545
4546 /**
4547  * g_app_info_get_all:
4548  *
4549  * Gets a list of all of the applications currently registered
4550  * on this system.
4551  *
4552  * For desktop files, this includes applications that have
4553  * `NoDisplay=true` set or are excluded from display by means
4554  * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4555  * The returned list does not include applications which have
4556  * the `Hidden` key set.
4557  *
4558  * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4559  **/
4560 GList *
4561 g_app_info_get_all (void)
4562 {
4563   GHashTable *apps;
4564   GHashTableIter iter;
4565   gpointer value;
4566   int i;
4567   GList *infos;
4568
4569   apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4570
4571   desktop_file_dirs_lock ();
4572
4573   for (i = 0; i < desktop_file_dirs->len; i++)
4574     desktop_file_dir_get_all (g_ptr_array_index (desktop_file_dirs, i), apps);
4575
4576   desktop_file_dirs_unlock ();
4577
4578   infos = NULL;
4579   g_hash_table_iter_init (&iter, apps);
4580   while (g_hash_table_iter_next (&iter, NULL, &value))
4581     {
4582       if (value)
4583         infos = g_list_prepend (infos, value);
4584     }
4585
4586   g_hash_table_destroy (apps);
4587
4588   return infos;
4589 }
4590
4591 /* GDesktopAppInfoLookup interface {{{2 */
4592
4593 /**
4594  * GDesktopAppInfoLookup:
4595  *
4596  * #GDesktopAppInfoLookup is an opaque data structure and can only be accessed
4597  * using the following functions.
4598  *
4599  * Deprecated: 2.28: The #GDesktopAppInfoLookup interface is deprecated and
4600  *    unused by GIO.
4601  **/
4602
4603 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4604
4605 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface;
4606 G_DEFINE_INTERFACE (GDesktopAppInfoLookup, g_desktop_app_info_lookup, G_TYPE_OBJECT)
4607
4608 static void
4609 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface)
4610 {
4611 }
4612
4613 /* "Get for mime type" APIs {{{2 */
4614
4615 /**
4616  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4617  * @lookup: a #GDesktopAppInfoLookup
4618  * @uri_scheme: a string containing a URI scheme.
4619  *
4620  * Gets the default application for launching applications
4621  * using this URI scheme for a particular #GDesktopAppInfoLookup
4622  * implementation.
4623  *
4624  * The #GDesktopAppInfoLookup interface and this function is used
4625  * to implement g_app_info_get_default_for_uri_scheme() backends
4626  * in a GIO module. There is no reason for applications to use it
4627  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4628  *
4629  * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4630  *
4631  * Deprecated: 2.28: The #GDesktopAppInfoLookup interface is deprecated and
4632  *    unused by GIO.
4633  */
4634 GAppInfo *
4635 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
4636                                                       const char            *uri_scheme)
4637 {
4638   GDesktopAppInfoLookupIface *iface;
4639
4640   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
4641
4642   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
4643
4644   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
4645 }
4646
4647 G_GNUC_END_IGNORE_DEPRECATIONS
4648
4649 /* Misc getter APIs {{{2 */
4650
4651 /**
4652  * g_desktop_app_info_get_startup_wm_class:
4653  * @info: a #GDesktopAppInfo that supports startup notify
4654  *
4655  * Retrieves the StartupWMClass field from @info. This represents the
4656  * WM_CLASS property of the main window of the application, if launched
4657  * through @info.
4658  *
4659  * Returns: (transfer none): the startup WM class, or %NULL if none is set
4660  * in the desktop file.
4661  *
4662  * Since: 2.34
4663  */
4664 const char *
4665 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo *info)
4666 {
4667   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4668
4669   return info->startup_wm_class;
4670 }
4671
4672 /**
4673  * g_desktop_app_info_get_string:
4674  * @info: a #GDesktopAppInfo
4675  * @key: the key to look up
4676  *
4677  * Looks up a string value in the keyfile backing @info.
4678  *
4679  * The @key is looked up in the "Desktop Entry" group.
4680  *
4681  * Returns: a newly allocated string, or %NULL if the key
4682  *     is not found
4683  *
4684  * Since: 2.36
4685  */
4686 char *
4687 g_desktop_app_info_get_string (GDesktopAppInfo *info,
4688                                const char      *key)
4689 {
4690   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4691
4692   return g_key_file_get_string (info->keyfile,
4693                                 G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4694 }
4695
4696 /**
4697  * g_desktop_app_info_get_locale_string:
4698  * @info: a #GDesktopAppInfo
4699  * @key: the key to look up
4700  *
4701  * Looks up a localized string value in the keyfile backing @info
4702  * translated to the current locale.
4703  *
4704  * The @key is looked up in the "Desktop Entry" group.
4705  *
4706  * Returns: (nullable): a newly allocated string, or %NULL if the key
4707  *     is not found
4708  *
4709  * Since: 2.56
4710  */
4711 char *
4712 g_desktop_app_info_get_locale_string (GDesktopAppInfo *info,
4713                                       const char      *key)
4714 {
4715   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4716   g_return_val_if_fail (key != NULL && *key != '\0', NULL);
4717
4718   return g_key_file_get_locale_string (info->keyfile,
4719                                        G_KEY_FILE_DESKTOP_GROUP,
4720                                        key, NULL, NULL);
4721 }
4722
4723 /**
4724  * g_desktop_app_info_get_boolean:
4725  * @info: a #GDesktopAppInfo
4726  * @key: the key to look up
4727  *
4728  * Looks up a boolean value in the keyfile backing @info.
4729  *
4730  * The @key is looked up in the "Desktop Entry" group.
4731  *
4732  * Returns: the boolean value, or %FALSE if the key
4733  *     is not found
4734  *
4735  * Since: 2.36
4736  */
4737 gboolean
4738 g_desktop_app_info_get_boolean (GDesktopAppInfo *info,
4739                                 const char      *key)
4740 {
4741   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
4742
4743   return g_key_file_get_boolean (info->keyfile,
4744                                  G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4745 }
4746
4747 /**
4748  * g_desktop_app_info_get_string_list:
4749  * @info: a #GDesktopAppInfo
4750  * @key: the key to look up
4751  * @length: (out) (optional): return location for the number of returned strings, or %NULL
4752  *
4753  * Looks up a string list value in the keyfile backing @info.
4754  *
4755  * The @key is looked up in the "Desktop Entry" group.
4756  *
4757  * Returns: (array zero-terminated=1 length=length) (element-type utf8) (transfer full):
4758  *  a %NULL-terminated string array or %NULL if the specified
4759  *  key cannot be found. The array should be freed with g_strfreev().
4760  *
4761  * Since: 2.60
4762  */
4763 gchar **
4764 g_desktop_app_info_get_string_list (GDesktopAppInfo *info,
4765                                     const char      *key,
4766                                     gsize           *length)
4767 {
4768   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4769
4770   return g_key_file_get_string_list (info->keyfile,
4771                                      G_KEY_FILE_DESKTOP_GROUP, key, length, NULL);
4772 }
4773
4774 /**
4775  * g_desktop_app_info_has_key:
4776  * @info: a #GDesktopAppInfo
4777  * @key: the key to look up
4778  *
4779  * Returns whether @key exists in the "Desktop Entry" group
4780  * of the keyfile backing @info.
4781  *
4782  * Returns: %TRUE if the @key exists
4783  *
4784  * Since: 2.36
4785  */
4786 gboolean
4787 g_desktop_app_info_has_key (GDesktopAppInfo *info,
4788                             const char      *key)
4789 {
4790   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
4791
4792   return g_key_file_has_key (info->keyfile,
4793                              G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4794 }
4795
4796 /* Desktop actions support {{{2 */
4797
4798 /**
4799  * g_desktop_app_info_list_actions:
4800  * @info: a #GDesktopAppInfo
4801  *
4802  * Returns the list of "additional application actions" supported on the
4803  * desktop file, as per the desktop file specification.
4804  *
4805  * As per the specification, this is the list of actions that are
4806  * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4807  *
4808  * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4809  *
4810  * Since: 2.38
4811  **/
4812 const gchar * const *
4813 g_desktop_app_info_list_actions (GDesktopAppInfo *info)
4814 {
4815   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4816
4817   return (const gchar **) info->actions;
4818 }
4819
4820 static gboolean
4821 app_info_has_action (GDesktopAppInfo *info,
4822                      const gchar     *action_name)
4823 {
4824   gint i;
4825
4826   for (i = 0; info->actions[i]; i++)
4827     if (g_str_equal (info->actions[i], action_name))
4828       return TRUE;
4829
4830   return FALSE;
4831 }
4832
4833 /**
4834  * g_desktop_app_info_get_action_name:
4835  * @info: a #GDesktopAppInfo
4836  * @action_name: the name of the action as from
4837  *   g_desktop_app_info_list_actions()
4838  *
4839  * Gets the user-visible display name of the "additional application
4840  * action" specified by @action_name.
4841  *
4842  * This corresponds to the "Name" key within the keyfile group for the
4843  * action.
4844  *
4845  * Returns: (transfer full): the locale-specific action name
4846  *
4847  * Since: 2.38
4848  */
4849 gchar *
4850 g_desktop_app_info_get_action_name (GDesktopAppInfo *info,
4851                                     const gchar     *action_name)
4852 {
4853   gchar *group_name;
4854   gchar *result;
4855
4856   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4857   g_return_val_if_fail (action_name != NULL, NULL);
4858   g_return_val_if_fail (app_info_has_action (info, action_name), NULL);
4859
4860   group_name = g_strdup_printf ("Desktop Action %s", action_name);
4861   result = g_key_file_get_locale_string (info->keyfile, group_name, "Name", NULL, NULL);
4862   g_free (group_name);
4863
4864   /* The spec says that the Name field must be given.
4865    *
4866    * If it's not, let's follow the behaviour of our get_name()
4867    * implementation above and never return %NULL.
4868    */
4869   if (result == NULL)
4870     result = g_strdup (_("Unnamed"));
4871
4872   return result;
4873 }
4874
4875 /**
4876  * g_desktop_app_info_launch_action:
4877  * @info: a #GDesktopAppInfo
4878  * @action_name: the name of the action as from
4879  *   g_desktop_app_info_list_actions()
4880  * @launch_context: (nullable): a #GAppLaunchContext
4881  *
4882  * Activates the named application action.
4883  *
4884  * You may only call this function on action names that were
4885  * returned from g_desktop_app_info_list_actions().
4886  *
4887  * Note that if the main entry of the desktop file indicates that the
4888  * application supports startup notification, and @launch_context is
4889  * non-%NULL, then startup notification will be used when activating the
4890  * action (and as such, invocation of the action on the receiving side
4891  * must signal the end of startup notification when it is completed).
4892  * This is the expected behaviour of applications declaring additional
4893  * actions, as per the desktop file specification.
4894  *
4895  * As with g_app_info_launch() there is no way to detect failures that
4896  * occur while using this function.
4897  *
4898  * Since: 2.38
4899  */
4900 void
4901 g_desktop_app_info_launch_action (GDesktopAppInfo   *info,
4902                                   const gchar       *action_name,
4903                                   GAppLaunchContext *launch_context)
4904 {
4905   GDBusConnection *session_bus;
4906
4907   g_return_if_fail (G_IS_DESKTOP_APP_INFO (info));
4908   g_return_if_fail (action_name != NULL);
4909   g_return_if_fail (app_info_has_action (info, action_name));
4910
4911   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
4912
4913   if (session_bus && info->app_id)
4914     {
4915       gchar *object_path;
4916
4917       object_path = object_path_from_appid (info->app_id);
4918       g_dbus_connection_call (session_bus, info->app_id, object_path,
4919                               "org.freedesktop.Application", "ActivateAction",
4920                               g_variant_new ("(sav@a{sv})", action_name, NULL,
4921                                              g_desktop_app_info_make_platform_data (info, NULL, launch_context)),
4922                               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
4923       g_free (object_path);
4924     }
4925   else
4926     {
4927       gchar *group_name;
4928       gchar *exec_line;
4929
4930       group_name = g_strdup_printf ("Desktop Action %s", action_name);
4931       exec_line = g_key_file_get_string (info->keyfile, group_name, "Exec", NULL);
4932       g_free (group_name);
4933
4934       if (exec_line)
4935         g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
4936                                                    _SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL,
4937                                                    -1, -1, -1, NULL);
4938
4939       g_free (exec_line);
4940     }
4941
4942   if (session_bus != NULL)
4943     {
4944       g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
4945       g_object_unref (session_bus);
4946     }
4947 }
4948 /* Epilogue {{{1 */
4949
4950 /* vim:set foldmethod=marker: */