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