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