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