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