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