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