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