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