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