Support desktop file key X-GIO-NoFuse which disables use of fuse pathnames
[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
571   g_return_if_fail (exec != NULL);
572
573   /* On %u and %U, pass POSIX file path pointing to the URI via
574    * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
575    * running or the URI doesn't have a POSIX file path via FUSE
576    * we'll just pass the URI.
577    */
578   force_file_uri_macro = macro;
579   force_file_uri = FALSE;
580   if (!info->no_fuse)
581     {
582       switch (macro)
583         {
584         case 'u':
585           force_file_uri_macro = 'f';
586           force_file_uri = TRUE;
587           break;
588         case 'U':
589           force_file_uri_macro = 'F';
590           force_file_uri = TRUE;
591           break;
592         default:
593           break;
594         }
595     }
596
597   switch (macro)
598     {
599     case 'u':
600     case 'f':
601     case 'd':
602     case 'n':
603       if (uris)
604         {
605           if (!force_file_uri)
606             {
607               expanded = expand_macro_single (macro, uris->data);
608             }
609           else
610             {
611               expanded = expand_macro_single (force_file_uri_macro, uris->data);
612               if (expanded == NULL)
613                 expanded = expand_macro_single (macro, uris->data);
614             }
615
616           if (expanded)
617             {
618               g_string_append (exec, expanded);
619               g_free (expanded);
620             }
621           uris = uris->next;
622         }
623
624       break;
625
626     case 'U':   
627     case 'F':
628     case 'D':
629     case 'N':
630       while (uris)
631         {
632           if (!force_file_uri)
633             {
634               expanded = expand_macro_single (macro, uris->data);
635             }
636           else
637             {
638               expanded = expand_macro_single (force_file_uri_macro, uris->data);
639               if (expanded == NULL)
640                 expanded = expand_macro_single (macro, uris->data);
641             }
642
643           if (expanded)
644             {
645               g_string_append (exec, expanded);
646               g_free (expanded);
647             }
648           
649           uris = uris->next;
650           
651           if (uris != NULL && expanded)
652             g_string_append_c (exec, ' ');
653         }
654
655       break;
656
657     case 'i':
658       if (info->icon_name)
659         {
660           g_string_append (exec, "--icon ");
661           g_string_append (exec, info->icon_name);
662         }
663       break;
664
665     case 'c':
666       if (info->name) 
667         g_string_append (exec, info->name);
668       break;
669
670     case 'k':
671       if (info->filename) 
672         g_string_append (exec, info->filename);
673       break;
674
675     case 'm': /* deprecated */
676       break;
677
678     case '%':
679       g_string_append_c (exec, '%');
680       break;
681     }
682   
683   *uri_list = uris;
684 }
685
686 static gboolean
687 expand_application_parameters (GDesktopAppInfo   *info,
688                                GList            **uris,
689                                int               *argc,
690                                char            ***argv,
691                                GError           **error)
692 {
693   GList *uri_list = *uris;
694   const char *p = info->exec;
695   GString *expanded_exec = g_string_new (NULL);
696   gboolean res;
697   
698   if (info->exec == NULL)
699     {
700       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
701                            _("Desktop file didn't specify Exec field"));
702       return FALSE;
703     }
704   
705   while (*p)
706     {
707       if (p[0] == '%' && p[1] != '\0')
708         {
709           expand_macro (p[1], expanded_exec, info, uris);
710           p++;
711         }
712       else
713         g_string_append_c (expanded_exec, *p);
714       
715       p++;
716     }
717   
718   /* No file substitutions */
719   if (uri_list == *uris && uri_list != NULL)
720     {
721       /* If there is no macro default to %f. This is also what KDE does */
722       g_string_append_c (expanded_exec, ' ');
723       expand_macro ('f', expanded_exec, info, uris);
724     }
725   
726   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
727   g_string_free (expanded_exec, TRUE);
728   return res;
729 }
730
731 static gboolean
732 prepend_terminal_to_vector (int    *argc,
733                             char ***argv)
734 {
735 #ifndef G_OS_WIN32
736   char **real_argv;
737   int real_argc;
738   int i, j;
739   char **term_argv = NULL;
740   int term_argc = 0;
741   char *check;
742   char **the_argv;
743   
744   g_return_val_if_fail (argc != NULL, FALSE);
745   g_return_val_if_fail (argv != NULL, FALSE);
746         
747   /* sanity */
748   if(*argv == NULL)
749     *argc = 0;
750   
751   the_argv = *argv;
752
753   /* compute size if not given */
754   if (*argc < 0)
755     {
756       for (i = 0; the_argv[i] != NULL; i++)
757         ;
758       *argc = i;
759     }
760   
761   term_argc = 2;
762   term_argv = g_new0 (char *, 3);
763
764   check = g_find_program_in_path ("gnome-terminal");
765   if (check != NULL)
766     {
767       term_argv[0] = check;
768       /* Note that gnome-terminal takes -x and
769        * as -e in gnome-terminal is broken we use that. */
770       term_argv[1] = g_strdup ("-x");
771     }
772   else
773     {
774       if (check == NULL)
775         check = g_find_program_in_path ("nxterm");
776       if (check == NULL)
777         check = g_find_program_in_path ("color-xterm");
778       if (check == NULL)
779         check = g_find_program_in_path ("rxvt");
780       if (check == NULL)
781         check = g_find_program_in_path ("xterm");
782       if (check == NULL)
783         check = g_find_program_in_path ("dtterm");
784       if (check == NULL)
785         {
786           check = g_strdup ("xterm");
787           g_warning ("couldn't find a terminal, falling back to xterm");
788         }
789       term_argv[0] = check;
790       term_argv[1] = g_strdup ("-e");
791     }
792
793   real_argc = term_argc + *argc;
794   real_argv = g_new (char *, real_argc + 1);
795   
796   for (i = 0; i < term_argc; i++)
797     real_argv[i] = term_argv[i];
798   
799   for (j = 0; j < *argc; j++, i++)
800     real_argv[i] = (char *)the_argv[j];
801   
802   real_argv[i] = NULL;
803   
804   g_free (*argv);
805   *argv = real_argv;
806   *argc = real_argc;
807   
808   /* we use g_free here as we sucked all the inner strings
809    * out from it into real_argv */
810   g_free (term_argv);
811   return TRUE;
812 #else
813   return FALSE;
814 #endif /* G_OS_WIN32 */
815 }
816
817 /* '=' is the new '\0'.
818  * DO NOT CALL unless at least one string ends with '='
819  */
820 static gboolean
821 is_env (const char *a,
822         const char *b)
823 {
824   while (*a == *b)
825   {
826     if (*a == 0 || *b == 0)
827       return FALSE;
828     
829     if (*a == '=')
830       return TRUE;
831
832     a++;
833     b++;
834   }
835
836   return FALSE;
837 }
838
839 /* free with g_strfreev */
840 static char **
841 replace_env_var (char       **old_environ,
842                  const char  *env_var,
843                  const char  *new_value)
844 {
845   int length, new_length;
846   int index, new_index;
847   char **new_environ;
848   int i, new_i;
849
850   /* do two things at once:
851    *  - discover the length of the environment ('length')
852    *  - find the location (if any) of the env var ('index')
853    */
854   index = -1;
855   for (length = 0; old_environ[length]; length++)
856     {
857       /* if we already have it in our environment, replace */
858       if (is_env (old_environ[length], env_var))
859         index = length;
860     }
861
862   
863   /* no current env var, no desired env value.
864    * this is easy :)
865    */
866   if (new_value == NULL && index == -1)
867     return old_environ;
868
869   /* in all cases now, we will be using a modified environment.
870    * determine its length and allocated it.
871    * 
872    * after this block:
873    *   new_index   = location to insert, if any
874    *   new_length  = length of the new array
875    *   new_environ = the pointer array for the new environment
876    */
877   
878   if (new_value == NULL && index >= 0)
879     {
880       /* in this case, we will be removing an entry */
881       new_length = length - 1;
882       new_index = -1;
883     }
884   else if (new_value != NULL && index < 0)
885     {
886       /* in this case, we will be adding an entry to the end */
887       new_length = length + 1;
888       new_index = length;
889     }
890   else
891     /* in this case, we will be replacing the existing entry */
892     {
893       new_length = length;
894       new_index = index;
895     }
896
897   new_environ = g_malloc (sizeof (char *) * (new_length + 1));
898   new_environ[new_length] = NULL;
899
900   /* now we do the copying.
901    * for each entry in the new environment, we decide what to do
902    */
903   
904   i = 0;
905   for (new_i = 0; new_i < new_length; new_i++)
906     {
907       if (new_i == new_index)
908         {
909           /* insert our new item */
910           new_environ[new_i] = g_strconcat (env_var,
911                                             "=",
912                                             new_value,
913                                             NULL);
914           
915           /* if we had an old entry, skip it now */
916           if (index >= 0)
917             i++;
918         }
919       else
920         {
921           /* if this is the old DESKTOP_STARTUP_ID, skip it */
922           if (i == index)
923             i++;
924           
925           /* copy an old item */
926           new_environ[new_i] = g_strdup (old_environ[i]);
927           i++;
928         }
929     }
930
931   g_strfreev (old_environ);
932   
933   return new_environ;
934 }
935
936 static GList *
937 uri_list_segment_to_files (GList *start,
938                            GList *end)
939 {
940   GList *res;
941   GFile *file;
942
943   res = NULL;
944   while (start != NULL && start != end)
945     {
946       file = g_file_new_for_uri ((char *)start->data);
947       res = g_list_prepend (res, file);
948       start = start->next;
949     }
950
951   return g_list_reverse (res);
952 }
953
954 #ifdef HAVE__NSGETENVIRON
955 #define environ (*_NSGetEnviron())
956 #elif !defined(G_OS_WIN32)
957
958 /* According to the Single Unix Specification, environ is not in 
959  *  * any system header, although unistd.h often declares it.
960  *   */
961 extern char **environ;
962 #endif
963
964 static gboolean
965 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
966                                 GList              *uris,
967                                 GAppLaunchContext  *launch_context,
968                                 GError            **error)
969 {
970   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
971   gboolean completed = FALSE;
972   GList *old_uris;
973   GList *launched_files;
974   char **envp;
975   char **argv;
976   int argc;
977   char *display;
978   char *sn_id;
979
980   g_return_val_if_fail (appinfo != NULL, FALSE);
981
982   argv = NULL;
983   envp = NULL;
984       
985   do 
986     {
987       old_uris = uris;
988       if (!expand_application_parameters (info, &uris,
989                                           &argc, &argv, error))
990         goto out;
991       
992       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
993         {
994           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
995                                _("Unable to find terminal required for application"));
996           goto out;
997         }
998
999       sn_id = NULL;
1000       if (launch_context)
1001         {
1002           launched_files = uri_list_segment_to_files (old_uris, uris);
1003           
1004           display = g_app_launch_context_get_display (launch_context,
1005                                                       appinfo,
1006                                                       launched_files);
1007
1008           sn_id = NULL;
1009           if (info->startup_notify)
1010             sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
1011                                                                 appinfo,
1012                                                                 launched_files);
1013           
1014           if (display || sn_id)
1015             {
1016 #ifdef G_OS_WIN32
1017               /* FIXME */
1018               envp = g_new0 (char *, 1);
1019 #else
1020               envp = g_strdupv (environ);
1021 #endif
1022               
1023               if (display)
1024                 envp = replace_env_var (envp,
1025                                         "DISPLAY",
1026                                         display);
1027               
1028               if (sn_id)
1029                 envp = replace_env_var (envp,
1030                                         "DESKTOP_STARTUP_ID",
1031                                         sn_id);
1032             }
1033
1034           g_free (display);
1035           
1036           g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1037           g_list_free (launched_files);
1038         }
1039       
1040       if (!g_spawn_async (info->path,  /* working directory */
1041                           argv,
1042                           envp,
1043                           G_SPAWN_SEARCH_PATH /* flags */,
1044                           NULL /* child_setup */,
1045                           NULL /* data */,
1046                           NULL /* child_pid */,
1047                           error))
1048         {
1049           if (sn_id)
1050             {
1051               g_app_launch_context_launch_failed (launch_context, sn_id);
1052               g_free (sn_id);
1053             }
1054           goto out;
1055         }
1056
1057       
1058       g_free (sn_id);
1059       
1060       g_strfreev (envp);
1061       g_strfreev (argv);
1062       envp = NULL;
1063       argv = NULL;
1064     }
1065   while (uris != NULL);
1066
1067   completed = TRUE;
1068
1069  out:
1070   g_strfreev (argv);
1071   g_strfreev (envp);
1072
1073   return completed;
1074 }
1075
1076 static gboolean
1077 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1078 {
1079   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1080  
1081   return info->exec && 
1082     ((strstr (info->exec, "%u") != NULL) ||
1083      (strstr (info->exec, "%U") != NULL));
1084 }
1085
1086 static gboolean
1087 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1088 {
1089   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1090  
1091   return info->exec && 
1092     ((strstr (info->exec, "%f") != NULL) ||
1093      (strstr (info->exec, "%F") != NULL));
1094 }
1095
1096 static gboolean
1097 g_desktop_app_info_launch (GAppInfo           *appinfo,
1098                            GList              *files,
1099                            GAppLaunchContext  *launch_context,
1100                            GError            **error)
1101 {
1102   GList *uris;
1103   char *uri;
1104   gboolean res;
1105
1106   uris = NULL;
1107   while (files)
1108     {
1109       uri = g_file_get_uri (files->data);
1110       uris = g_list_prepend (uris, uri);
1111       files = files->next;
1112     }
1113   
1114   uris = g_list_reverse (uris);
1115   
1116   res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1117   
1118   g_list_foreach  (uris, (GFunc)g_free, NULL);
1119   g_list_free (uris);
1120   
1121   return res;
1122 }
1123
1124 G_LOCK_DEFINE_STATIC (g_desktop_env);
1125 static gchar *g_desktop_env = NULL;
1126
1127 /**
1128  * g_desktop_app_info_set_desktop_env:
1129  * @desktop_env: a string specifying what desktop this is
1130  *
1131  * Sets the name of the desktop that the application is running in.
1132  * This is used by g_app_info_should_show() to evaluate the
1133  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1134  * desktop entry fields.
1135  *
1136  * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop 
1137  * Menu specification</ulink> recognizes the following:
1138  * <simplelist>
1139  *   <member>GNOME</member>
1140  *   <member>KDE</member>
1141  *   <member>ROX</member>
1142  *   <member>XFCE</member>
1143  *   <member>Old</member> 
1144  * </simplelist>
1145  *
1146  * Should be called only once; subsequent calls are ignored.
1147  */
1148 void
1149 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1150 {
1151   G_LOCK (g_desktop_env);
1152   if (!g_desktop_env)
1153     g_desktop_env = g_strdup (desktop_env);
1154   G_UNLOCK (g_desktop_env);
1155 }
1156
1157 static gboolean
1158 g_desktop_app_info_should_show (GAppInfo *appinfo)
1159 {
1160   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1161   gboolean found;
1162   const gchar *desktop_env;
1163   int i;
1164
1165   if (info->nodisplay)
1166     return FALSE;
1167
1168   G_LOCK (g_desktop_env);
1169   desktop_env = g_desktop_env;
1170   G_UNLOCK (g_desktop_env);
1171
1172   if (info->only_show_in)
1173     {
1174       if (desktop_env == NULL)
1175         return FALSE;
1176       
1177       found = FALSE;
1178       for (i = 0; info->only_show_in[i] != NULL; i++)
1179         {
1180           if (strcmp (info->only_show_in[i], desktop_env) == 0)
1181             {
1182               found = TRUE;
1183               break;
1184             }
1185         }
1186       if (!found)
1187         return FALSE;
1188     }
1189
1190   if (info->not_show_in && desktop_env)
1191     {
1192       for (i = 0; info->not_show_in[i] != NULL; i++)
1193         {
1194           if (strcmp (info->not_show_in[i], desktop_env) == 0)
1195             return FALSE;
1196         }
1197     }
1198   
1199   return TRUE;
1200 }
1201
1202 typedef enum {
1203   APP_DIR,
1204   MIMETYPE_DIR
1205 } DirType;
1206
1207 static char *
1208 ensure_dir (DirType   type,
1209             GError  **error)
1210 {
1211   char *path, *display_name;
1212   int errsv;
1213
1214   if (type == APP_DIR)
1215     path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1216   else
1217     path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1218
1219   errno = 0;
1220   if (g_mkdir_with_parents (path, 0700) == 0)
1221     return path;
1222
1223   errsv = errno;
1224   display_name = g_filename_display_name (path);
1225   if (type == APP_DIR)
1226     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1227                  _("Can't create user application configuration folder %s: %s"),
1228                  display_name, g_strerror (errsv));
1229   else
1230     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1231                  _("Can't create user MIME configuration folder %s: %s"),
1232                  display_name, g_strerror (errsv));
1233
1234   g_free (display_name);
1235   g_free (path);
1236
1237   return NULL;
1238 }
1239
1240 static gboolean
1241 update_mimeapps_list (const char  *desktop_id, 
1242                       const char  *content_type, 
1243                       gboolean     add_as_default,
1244                       gboolean     add_non_default,
1245                       gboolean     remove, 
1246                       GError     **error)
1247 {
1248   char *dirname, *filename;
1249   GKeyFile *key_file;
1250   gboolean load_succeeded, res;
1251   char **old_list, **list;
1252   GList *system_list, *l;
1253   gsize length, data_size;
1254   char *data;
1255   int i, j, k;
1256   char **content_types;
1257
1258   /* Don't add both at start and end */
1259   g_assert (!(add_as_default && add_non_default));
1260   
1261   dirname = ensure_dir (APP_DIR, error);
1262   if (!dirname)
1263     return FALSE;
1264
1265   filename = g_build_filename (dirname, "mimeapps.list", NULL);
1266   g_free (dirname);
1267
1268   key_file = g_key_file_new ();
1269   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1270   if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1271     {
1272       g_key_file_free (key_file);
1273       key_file = g_key_file_new ();
1274     }
1275
1276   if (content_type)
1277     {
1278       content_types = g_new (char *, 2);
1279       content_types[0] = g_strdup (content_type);
1280       content_types[1] = NULL;
1281     }
1282   else
1283     {
1284       content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1285     }
1286
1287   for (k = 0; content_types && content_types[k]; k++)
1288     { 
1289       /* Add to the right place in the list */
1290   
1291       length = 0;
1292       old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1293                                              content_types[k], &length, NULL);
1294
1295       list = g_new (char *, 1 + length + 1);
1296
1297       i = 0;
1298       if (add_as_default)
1299         list[i++] = g_strdup (desktop_id);
1300       if (old_list)
1301         {
1302           for (j = 0; old_list[j] != NULL; j++)
1303             {
1304               if (g_strcmp0 (old_list[j], desktop_id) != 0)
1305                 list[i++] = g_strdup (old_list[j]);
1306               else if (add_non_default)
1307                 {
1308                   /* If adding as non-default, and it's already in,
1309                      don't change order of desktop ids */
1310                   add_non_default = FALSE;
1311                   list[i++] = g_strdup (old_list[j]);
1312                 }
1313             }
1314         }
1315       
1316       if (add_non_default)
1317         {
1318           /* We're adding as non-default, and it wasn't already in the list,
1319              so we add at the end. But to avoid listing the app before the
1320              current system default (thus changing the default) we have to
1321              add the current list of (not yet listed) apps before it. */
1322
1323           list[i] = NULL; /* Terminate current list so we can use it */
1324           system_list =  get_all_desktop_entries_for_mime_type (content_type, (const char **)list);
1325           
1326           list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1327           
1328           for (l = system_list; l != NULL; l = l->next)
1329             {
1330               list[i++] = l->data; /* no strdup, taking ownership */
1331               if (g_strcmp0 (l->data, desktop_id) == 0)
1332                 add_non_default = FALSE;
1333             }
1334           g_list_free (system_list);
1335                   
1336           if (add_non_default)
1337             list[i++] = g_strdup (desktop_id);
1338         }
1339       
1340       list[i] = NULL;
1341   
1342       g_strfreev (old_list);
1343
1344       if (list[0] == NULL || desktop_id == NULL)
1345         g_key_file_remove_key (key_file,
1346                                ADDED_ASSOCIATIONS_GROUP,
1347                                content_types[k],
1348                                NULL);
1349       else
1350         g_key_file_set_string_list (key_file,
1351                                     ADDED_ASSOCIATIONS_GROUP,
1352                                     content_types[k],
1353                                     (const char * const *)list, i);
1354    
1355       g_strfreev (list);
1356     }
1357   
1358   if (content_type)
1359     {
1360       /* reuse the list from above */
1361     }
1362   else
1363     {
1364       g_strfreev (content_types);
1365       content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1366     }
1367
1368   for (k = 0; content_types && content_types[k]; k++) 
1369     {
1370       /* Remove from removed associations group (unless remove) */
1371   
1372       length = 0;
1373       old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1374                                              content_types[k], &length, NULL);
1375
1376       list = g_new (char *, 1 + length + 1);
1377
1378       i = 0;
1379       if (remove)
1380         list[i++] = g_strdup (desktop_id);
1381       if (old_list)
1382         {
1383           for (j = 0; old_list[j] != NULL; j++)
1384             {
1385               if (g_strcmp0 (old_list[j], desktop_id) != 0)
1386                 list[i++] = g_strdup (old_list[j]);
1387             }
1388         }
1389       list[i] = NULL;
1390   
1391       g_strfreev (old_list);
1392
1393       if (list[0] == NULL || desktop_id == NULL)
1394         g_key_file_remove_key (key_file,
1395                                REMOVED_ASSOCIATIONS_GROUP,
1396                                content_types[k],
1397                                NULL);
1398       else
1399         g_key_file_set_string_list (key_file,
1400                                     REMOVED_ASSOCIATIONS_GROUP,
1401                                     content_types[k],
1402                                     (const char * const *)list, i);
1403
1404       g_strfreev (list);
1405     }
1406   
1407   g_strfreev (content_types);  
1408
1409   data = g_key_file_to_data (key_file, &data_size, error);
1410   g_key_file_free (key_file);
1411   
1412   res = g_file_set_contents (filename, data, data_size, error);
1413
1414   mime_info_cache_reload (NULL);
1415                           
1416   g_free (filename);
1417   g_free (data);
1418   
1419   return res;
1420 }
1421
1422 static gboolean
1423 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
1424                                             const char  *content_type,
1425                                             GError     **error)
1426 {
1427   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1428
1429   if (!g_desktop_app_info_ensure_saved (info, error))
1430     return FALSE;  
1431   
1432   return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1433 }
1434
1435 static void
1436 update_program_done (GPid     pid,
1437                      gint     status,
1438                      gpointer data)
1439 {
1440   /* Did the application exit correctly */
1441   if (WIFEXITED (status) &&
1442       WEXITSTATUS (status) == 0)
1443     {
1444       /* Here we could clean out any caches in use */
1445     }
1446 }
1447
1448 static void
1449 run_update_command (char *command,
1450                     char *subdir)
1451 {
1452         char *argv[3] = {
1453                 NULL,
1454                 NULL,
1455                 NULL,
1456         };
1457         GPid pid = 0;
1458         GError *error = NULL;
1459
1460         argv[0] = command;
1461         argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1462
1463         if (g_spawn_async ("/", argv,
1464                            NULL,       /* envp */
1465                            G_SPAWN_SEARCH_PATH |
1466                            G_SPAWN_STDOUT_TO_DEV_NULL |
1467                            G_SPAWN_STDERR_TO_DEV_NULL |
1468                            G_SPAWN_DO_NOT_REAP_CHILD,
1469                            NULL, NULL, /* No setup function */
1470                            &pid,
1471                            &error)) 
1472           g_child_watch_add (pid, update_program_done, NULL);
1473         else
1474           {
1475             /* If we get an error at this point, it's quite likely the user doesn't
1476              * have an installed copy of either 'update-mime-database' or
1477              * 'update-desktop-database'.  I don't think we want to popup an error
1478              * dialog at this point, so we just do a g_warning to give the user a
1479              * chance of debugging it.
1480              */
1481             g_warning ("%s", error->message);
1482           }
1483         
1484         g_free (argv[1]);
1485 }
1486
1487 static gboolean
1488 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
1489                                                  const char  *extension,
1490                                                  GError     **error)
1491 {
1492   char *filename, *basename, *mimetype;
1493   char *dirname;
1494   gboolean res;
1495
1496   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1497     return FALSE;  
1498   
1499   dirname = ensure_dir (MIMETYPE_DIR, error);
1500   if (!dirname)
1501     return FALSE;
1502   
1503   basename = g_strdup_printf ("user-extension-%s.xml", extension);
1504   filename = g_build_filename (dirname, basename, NULL);
1505   g_free (basename);
1506   g_free (dirname);
1507
1508   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1509   
1510   if (!g_file_test (filename, G_FILE_TEST_EXISTS)) 
1511     {
1512       char *contents;
1513
1514       contents =
1515         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1516                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1517                          " <mime-type type=\"%s\">\n"
1518                          "  <comment>%s document</comment>\n"
1519                          "  <glob pattern=\"*.%s\"/>\n"
1520                          " </mime-type>\n"
1521                          "</mime-info>\n", mimetype, extension, extension);
1522
1523       g_file_set_contents (filename, contents, -1, NULL);
1524       g_free (contents);
1525
1526       run_update_command ("update-mime-database", "mime");
1527     }
1528   g_free (filename);
1529   
1530   res = g_desktop_app_info_set_as_default_for_type (appinfo,
1531                                                     mimetype,
1532                                                     error);
1533
1534   g_free (mimetype);
1535   
1536   return res;
1537 }
1538
1539 static gboolean
1540 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
1541                                       const char  *content_type,
1542                                       GError     **error)
1543 {
1544   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1545
1546   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1547     return FALSE;  
1548   
1549   return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1550 }
1551
1552 static gboolean
1553 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1554 {
1555   return TRUE;
1556 }
1557
1558 static gboolean
1559 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
1560                                          const char  *content_type,
1561                                          GError     **error)
1562 {
1563   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1564
1565   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1566     return FALSE;
1567   
1568   return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1569 }
1570
1571 static gboolean
1572 g_desktop_app_info_ensure_saved (GDesktopAppInfo  *info,
1573                                  GError          **error)
1574 {
1575   GKeyFile *key_file;
1576   char *dirname;
1577   char *filename;
1578   char *data, *desktop_id;
1579   gsize data_size;
1580   int fd;
1581   gboolean res;
1582   
1583   if (info->filename != NULL)
1584     return TRUE;
1585
1586   /* This is only used for object created with
1587    * g_app_info_create_from_commandline. All other
1588    * object should have a filename
1589    */
1590   
1591   dirname = ensure_dir (APP_DIR, error);
1592   if (!dirname)
1593     return FALSE;
1594   
1595   key_file = g_key_file_new ();
1596
1597   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1598                          "Encoding", "UTF-8");
1599   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1600                          G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1601   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1602                          G_KEY_FILE_DESKTOP_KEY_TYPE,
1603                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1604   if (info->terminal) 
1605     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1606                             G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1607
1608   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1609                          G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1610
1611   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1612                          G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1613
1614   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1615                          G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1616   
1617   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1618                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1619
1620   data = g_key_file_to_data (key_file, &data_size, NULL);
1621   g_key_file_free (key_file);
1622
1623   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1624   filename = g_build_filename (dirname, desktop_id, NULL);
1625   g_free (desktop_id);
1626   g_free (dirname);
1627   
1628   fd = g_mkstemp (filename);
1629   if (fd == -1)
1630     {
1631       char *display_name;
1632
1633       display_name = g_filename_display_name (filename);
1634       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1635                    _("Can't create user desktop file %s"), display_name);
1636       g_free (display_name);
1637       g_free (filename);
1638       g_free (data);
1639       return FALSE;
1640     }
1641
1642   desktop_id = g_path_get_basename (filename);
1643
1644   close (fd);
1645   
1646   res = g_file_set_contents (filename, data, data_size, error);
1647   if (!res)
1648     {
1649       g_free (desktop_id);
1650       g_free (filename);
1651       return FALSE;
1652     }
1653
1654   info->filename = filename;
1655   info->desktop_id = desktop_id;
1656   
1657   run_update_command ("update-desktop-database", "applications");
1658   
1659   return TRUE;
1660 }
1661
1662 static gboolean
1663 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1664 {
1665   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1666
1667   if (info->filename)
1668     {
1669       if (strstr (info->filename, "/userapp-"))
1670         return g_access (info->filename, W_OK) == 0;
1671     }
1672
1673   return FALSE;
1674 }
1675
1676 static gboolean
1677 g_desktop_app_info_delete (GAppInfo *appinfo)
1678 {
1679   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1680   
1681   if (info->filename)
1682     { 
1683       if (g_remove (info->filename) == 0)
1684         {
1685           update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1686
1687           g_free (info->filename);
1688           info->filename = NULL;
1689           g_free (info->desktop_id);
1690           info->desktop_id = NULL;
1691
1692           return TRUE;
1693         }
1694     }
1695
1696   return FALSE;
1697 }
1698
1699 /**
1700  * g_app_info_create_from_commandline:
1701  * @commandline: the commandline to use
1702  * @application_name: the application name, or %NULL to use @commandline
1703  * @flags: flags that can specify details of the created #GAppInfo
1704  * @error: a #GError location to store the error occuring, %NULL to ignore.
1705  *
1706  * Creates a new #GAppInfo from the given information.
1707  *
1708  * Returns: new #GAppInfo for given command.
1709  **/
1710 GAppInfo *
1711 g_app_info_create_from_commandline (const char           *commandline,
1712                                     const char           *application_name,
1713                                     GAppInfoCreateFlags   flags,
1714                                     GError              **error)
1715 {
1716   char **split;
1717   char *basename;
1718   GDesktopAppInfo *info;
1719
1720   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1721
1722   info->filename = NULL;
1723   info->desktop_id = NULL;
1724   
1725   info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1726   info->startup_notify = FALSE;
1727   info->hidden = FALSE;
1728   if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1729     info->exec = g_strconcat (commandline, " %u", NULL);
1730   else
1731     info->exec = g_strconcat (commandline, " %f", NULL);
1732   info->nodisplay = TRUE;
1733   info->binary = binary_from_exec (info->exec);
1734   
1735   if (application_name)
1736     info->name = g_strdup (application_name);
1737   else
1738     {
1739       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1740       split = g_strsplit (commandline, " ", 2);
1741       basename = g_path_get_basename (split[0]);
1742       g_strfreev (split);
1743       info->name = basename;
1744       if (info->name == NULL)
1745         info->name = g_strdup ("custom");
1746     }
1747   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1748   
1749   return G_APP_INFO (info);
1750 }
1751
1752 static void
1753 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1754 {
1755   iface->dup = g_desktop_app_info_dup;
1756   iface->equal = g_desktop_app_info_equal;
1757   iface->get_id = g_desktop_app_info_get_id;
1758   iface->get_name = g_desktop_app_info_get_name;
1759   iface->get_description = g_desktop_app_info_get_description;
1760   iface->get_executable = g_desktop_app_info_get_executable;
1761   iface->get_icon = g_desktop_app_info_get_icon;
1762   iface->launch = g_desktop_app_info_launch;
1763   iface->supports_uris = g_desktop_app_info_supports_uris;
1764   iface->supports_files = g_desktop_app_info_supports_files;
1765   iface->launch_uris = g_desktop_app_info_launch_uris;
1766   iface->should_show = g_desktop_app_info_should_show;
1767   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1768   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1769   iface->add_supports_type = g_desktop_app_info_add_supports_type;
1770   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1771   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1772   iface->can_delete = g_desktop_app_info_can_delete;
1773   iface->do_delete = g_desktop_app_info_delete;
1774   iface->get_commandline = g_desktop_app_info_get_commandline;
1775 }
1776
1777 static gboolean
1778 app_info_in_list (GAppInfo *info, 
1779                   GList    *list)
1780 {
1781   while (list != NULL)
1782     {
1783       if (g_app_info_equal (info, list->data))
1784         return TRUE;
1785       list = list->next;
1786     }
1787   return FALSE;
1788 }
1789
1790
1791 /**
1792  * g_app_info_get_all_for_type:
1793  * @content_type: the content type to find a #GAppInfo for
1794  * 
1795  * Gets a list of all #GAppInfo s for a given content type.
1796  *
1797  * Returns: #GList of #GAppInfo s for given @content_type
1798  *    or %NULL on error.
1799  **/
1800 GList *
1801 g_app_info_get_all_for_type (const char *content_type)
1802 {
1803   GList *desktop_entries, *l;
1804   GList *infos;
1805   GDesktopAppInfo *info;
1806
1807   g_return_val_if_fail (content_type != NULL, NULL);
1808   
1809   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1810
1811   infos = NULL;
1812   for (l = desktop_entries; l != NULL; l = l->next)
1813     {
1814       char *desktop_entry = l->data;
1815
1816       info = g_desktop_app_info_new (desktop_entry);
1817       if (info)
1818         {
1819           if (app_info_in_list (G_APP_INFO (info), infos))
1820             g_object_unref (info);
1821           else
1822             infos = g_list_prepend (infos, info);
1823         }
1824       g_free (desktop_entry);
1825     }
1826
1827   g_list_free (desktop_entries);
1828   
1829   return g_list_reverse (infos);
1830 }
1831
1832 /**
1833  * g_app_info_reset_type_associations:
1834  * @content_type: a content type 
1835  *
1836  * Removes all changes to the type associations done by
1837  * g_app_info_set_as_default_for_type(), 
1838  * g_app_info_set_as_default_for_extension(), 
1839  * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1840  *
1841  * Since: 2.20
1842  */
1843 void
1844 g_app_info_reset_type_associations (const char *content_type)
1845 {
1846   update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1847 }
1848
1849 /**
1850  * g_app_info_get_default_for_type:
1851  * @content_type: the content type to find a #GAppInfo for
1852  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1853  *     support URIs
1854  * 
1855  * Gets the #GAppInfo that correspond to a given content type.
1856  *
1857  * Returns: #GAppInfo for given @content_type or %NULL on error.
1858  **/
1859 GAppInfo *
1860 g_app_info_get_default_for_type (const char *content_type,
1861                                  gboolean    must_support_uris)
1862 {
1863   GList *desktop_entries, *l;
1864   GAppInfo *info;
1865
1866   g_return_val_if_fail (content_type != NULL, NULL);
1867   
1868   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1869
1870   info = NULL;
1871   for (l = desktop_entries; l != NULL; l = l->next)
1872     {
1873       char *desktop_entry = l->data;
1874
1875       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1876       if (info)
1877         {
1878           if (must_support_uris && !g_app_info_supports_uris (info))
1879             {
1880               g_object_unref (info);
1881               info = NULL;
1882             }
1883           else
1884             break;
1885         }
1886     }
1887   
1888   g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
1889   g_list_free (desktop_entries);
1890   
1891   return info;
1892 }
1893
1894 /**
1895  * g_app_info_get_default_for_uri_scheme:
1896  * @uri_scheme: a string containing a URI scheme.
1897  *
1898  * Gets the default application for launching applications 
1899  * using this URI scheme. A URI scheme is the initial part 
1900  * of the URI, up to but not including the ':', e.g. "http", 
1901  * "ftp" or "sip".
1902  * 
1903  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1904  **/
1905 GAppInfo *
1906 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1907 {
1908   static gsize lookup = 0;
1909   
1910   if (g_once_init_enter (&lookup))
1911     {
1912       gsize setup_value = 1;
1913       GDesktopAppInfoLookup *lookup_instance;
1914       const char *use_this;
1915       GIOExtensionPoint *ep;
1916       GIOExtension *extension;
1917       GList *l;
1918
1919       use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1920       
1921       /* Ensure vfs in modules loaded */
1922       _g_io_modules_ensure_loaded ();
1923       
1924       ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1925
1926       lookup_instance = NULL;
1927       if (use_this)
1928         {
1929           extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1930           if (extension)
1931             lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1932         }
1933       
1934       if (lookup_instance == NULL)
1935         {
1936           for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1937             {
1938               extension = l->data;
1939               lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1940               if (lookup_instance != NULL)
1941                 break;
1942             }
1943         }
1944
1945       if (lookup_instance != NULL)
1946         setup_value = (gsize)lookup_instance;
1947       
1948       g_once_init_leave (&lookup, setup_value);
1949     }
1950
1951   if (lookup == 1)
1952     return NULL;
1953
1954   return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1955                                                                uri_scheme);
1956 }
1957
1958
1959 static void
1960 get_apps_from_dir (GHashTable *apps, 
1961                    const char *dirname, 
1962                    const char *prefix)
1963 {
1964   GDir *dir;
1965   const char *basename;
1966   char *filename, *subprefix, *desktop_id;
1967   gboolean hidden;
1968   GDesktopAppInfo *appinfo;
1969   
1970   dir = g_dir_open (dirname, 0, NULL);
1971   if (dir)
1972     {
1973       while ((basename = g_dir_read_name (dir)) != NULL)
1974         {
1975           filename = g_build_filename (dirname, basename, NULL);
1976           if (g_str_has_suffix (basename, ".desktop"))
1977             {
1978               desktop_id = g_strconcat (prefix, basename, NULL);
1979
1980               /* Use _extended so we catch NULLs too (hidden) */
1981               if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1982                 {
1983                   appinfo = g_desktop_app_info_new_from_filename (filename);
1984
1985                   if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1986                     {
1987                       g_object_unref (appinfo);
1988                       appinfo = NULL;
1989                       hidden = TRUE;
1990                     }
1991                                       
1992                   if (appinfo || hidden)
1993                     {
1994                       g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1995
1996                       if (appinfo)
1997                         {
1998                           /* Reuse instead of strdup here */
1999                           appinfo->desktop_id = desktop_id;
2000                           desktop_id = NULL;
2001                         }
2002                     }
2003                 }
2004               g_free (desktop_id);
2005             }
2006           else
2007             {
2008               if (g_file_test (filename, G_FILE_TEST_IS_DIR))
2009                 {
2010                   subprefix = g_strconcat (prefix, basename, "-", NULL);
2011                   get_apps_from_dir (apps, filename, subprefix);
2012                   g_free (subprefix);
2013                 }
2014             }
2015           g_free (filename);
2016         }
2017       g_dir_close (dir);
2018     }
2019 }
2020
2021
2022 /**
2023  * g_app_info_get_all:
2024  *
2025  * Gets a list of all of the applications currently registered 
2026  * on this system.
2027  * 
2028  * For desktop files, this includes applications that have 
2029  * <literal>NoDisplay=true</literal> set or are excluded from 
2030  * display by means of <literal>OnlyShowIn</literal> or
2031  * <literal>NotShowIn</literal>. See g_app_info_should_show().
2032  * The returned list does not include applications which have
2033  * the <literal>Hidden</literal> key set. 
2034  * 
2035  * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2036  **/
2037 GList *
2038 g_app_info_get_all (void)
2039 {
2040   const char * const *dirs;
2041   GHashTable *apps;
2042   GHashTableIter iter;
2043   gpointer value;
2044   int i;
2045   GList *infos;
2046
2047   dirs = get_applications_search_path ();
2048
2049   apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2050                                 g_free, NULL);
2051
2052   
2053   for (i = 0; dirs[i] != NULL; i++)
2054     get_apps_from_dir (apps, dirs[i], "");
2055
2056
2057   infos = NULL;
2058   g_hash_table_iter_init (&iter, apps);
2059   while (g_hash_table_iter_next (&iter, NULL, &value))
2060     {
2061       if (value)
2062         infos = g_list_prepend (infos, value);
2063     }
2064
2065   g_hash_table_destroy (apps);
2066
2067   return g_list_reverse (infos);
2068 }
2069
2070 /* Cacheing of mimeinfo.cache and defaults.list files */
2071
2072 typedef struct {
2073   char *path;
2074   GHashTable *mime_info_cache_map;
2075   GHashTable *defaults_list_map;
2076   GHashTable *mimeapps_list_added_map;
2077   GHashTable *mimeapps_list_removed_map;
2078   time_t mime_info_cache_timestamp;
2079   time_t defaults_list_timestamp;
2080   time_t mimeapps_list_timestamp;
2081 } MimeInfoCacheDir;
2082
2083 typedef struct {
2084   GList *dirs;                       /* mimeinfo.cache and defaults.list */
2085   GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2086   time_t last_stat_time;
2087   guint should_ping_mime_monitor : 1;
2088 } MimeInfoCache;
2089
2090 static MimeInfoCache *mime_info_cache = NULL;
2091 G_LOCK_DEFINE_STATIC (mime_info_cache);
2092
2093 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
2094                                                      const char        *mime_type,
2095                                                      char             **new_desktop_file_ids);
2096
2097 static MimeInfoCache * mime_info_cache_new (void);
2098
2099 static void
2100 destroy_info_cache_value (gpointer  key, 
2101                           GList    *value, 
2102                           gpointer  data)
2103 {
2104   g_list_foreach (value, (GFunc)g_free, NULL);
2105   g_list_free (value);
2106 }
2107
2108 static void
2109 destroy_info_cache_map (GHashTable *info_cache_map)
2110 {
2111   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2112   g_hash_table_destroy (info_cache_map);
2113 }
2114
2115 static gboolean
2116 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2117                                  const char       *cache_file,
2118                                  time_t           *timestamp)
2119 {
2120   struct stat buf;
2121   char *filename;
2122   
2123   filename = g_build_filename (dir->path, cache_file, NULL);
2124   
2125   if (g_stat (filename, &buf) < 0)
2126     {
2127       g_free (filename);
2128       return TRUE;
2129     }
2130   g_free (filename);
2131
2132   if (buf.st_mtime != *timestamp) 
2133     return TRUE;
2134   
2135   return FALSE;
2136 }
2137
2138 /* Call with lock held */
2139 static gboolean
2140 remove_all (gpointer  key,
2141             gpointer  value,
2142             gpointer  user_data)
2143 {
2144   return TRUE;
2145 }
2146
2147
2148 static void
2149 mime_info_cache_blow_global_cache (void)
2150 {
2151   g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2152                                remove_all, NULL);
2153 }
2154
2155 static void
2156 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2157 {
2158   GError *load_error;
2159   GKeyFile *key_file;
2160   gchar *filename, **mime_types;
2161   int i;
2162   struct stat buf;
2163   
2164   load_error = NULL;
2165   mime_types = NULL;
2166   
2167   if (dir->mime_info_cache_map != NULL &&
2168       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2169                                         &dir->mime_info_cache_timestamp))
2170     return;
2171   
2172   if (dir->mime_info_cache_map != NULL)
2173     destroy_info_cache_map (dir->mime_info_cache_map);
2174   
2175   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2176                                                     (GDestroyNotify) g_free,
2177                                                     NULL);
2178   
2179   key_file = g_key_file_new ();
2180   
2181   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2182   
2183   if (g_stat (filename, &buf) < 0)
2184     goto error;
2185   
2186   if (dir->mime_info_cache_timestamp > 0) 
2187     mime_info_cache->should_ping_mime_monitor = TRUE;
2188   
2189   dir->mime_info_cache_timestamp = buf.st_mtime;
2190   
2191   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2192   
2193   g_free (filename);
2194   filename = NULL;
2195   
2196   if (load_error != NULL)
2197     goto error;
2198   
2199   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2200                                     NULL, &load_error);
2201   
2202   if (load_error != NULL)
2203     goto error;
2204   
2205   for (i = 0; mime_types[i] != NULL; i++)
2206     {
2207       gchar **desktop_file_ids;
2208       char *unaliased_type;
2209       desktop_file_ids = g_key_file_get_string_list (key_file,
2210                                                      MIME_CACHE_GROUP,
2211                                                      mime_types[i],
2212                                                      NULL,
2213                                                      NULL);
2214       
2215       if (desktop_file_ids == NULL)
2216         continue;
2217
2218       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2219       mime_info_cache_dir_add_desktop_entries (dir,
2220                                                unaliased_type,
2221                                                desktop_file_ids);
2222       g_free (unaliased_type);
2223     
2224       g_strfreev (desktop_file_ids);
2225     }
2226   
2227   g_strfreev (mime_types);
2228   g_key_file_free (key_file);
2229   
2230   return;
2231  error:
2232   g_free (filename);
2233   g_key_file_free (key_file);
2234   
2235   if (mime_types != NULL)
2236     g_strfreev (mime_types);
2237   
2238   if (load_error)
2239     g_error_free (load_error);
2240 }
2241
2242 static void
2243 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2244 {
2245   GKeyFile *key_file;
2246   GError *load_error;
2247   gchar *filename, **mime_types;
2248   char *unaliased_type;
2249   char **desktop_file_ids;
2250   int i;
2251   struct stat buf;
2252
2253   load_error = NULL;
2254   mime_types = NULL;
2255
2256   if (dir->defaults_list_map != NULL &&
2257       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2258                                         &dir->defaults_list_timestamp))
2259     return;
2260   
2261   if (dir->defaults_list_map != NULL)
2262     g_hash_table_destroy (dir->defaults_list_map);
2263   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2264                                                   g_free, (GDestroyNotify)g_strfreev);
2265   
2266
2267   key_file = g_key_file_new ();
2268   
2269   filename = g_build_filename (dir->path, "defaults.list", NULL);
2270   if (g_stat (filename, &buf) < 0)
2271     goto error;
2272
2273   if (dir->defaults_list_timestamp > 0) 
2274     mime_info_cache->should_ping_mime_monitor = TRUE;
2275
2276   dir->defaults_list_timestamp = buf.st_mtime;
2277
2278   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2279   g_free (filename);
2280   filename = NULL;
2281
2282   if (load_error != NULL)
2283     goto error;
2284
2285   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2286                                     NULL, NULL);
2287   if (mime_types != NULL)
2288     {
2289       for (i = 0; mime_types[i] != NULL; i++)
2290         {
2291           desktop_file_ids = g_key_file_get_string_list (key_file,
2292                                                          DEFAULT_APPLICATIONS_GROUP,
2293                                                          mime_types[i],
2294                                                          NULL,
2295                                                          NULL);
2296           if (desktop_file_ids == NULL)
2297             continue;
2298           
2299           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2300           g_hash_table_replace (dir->defaults_list_map,
2301                                 unaliased_type,
2302                                 desktop_file_ids);
2303         }
2304       
2305       g_strfreev (mime_types);
2306     }
2307
2308   g_key_file_free (key_file);
2309   return;
2310   
2311  error:
2312   g_free (filename);
2313   g_key_file_free (key_file);
2314   
2315   if (mime_types != NULL)
2316     g_strfreev (mime_types);
2317   
2318   if (load_error)
2319     g_error_free (load_error);
2320 }
2321
2322 static void
2323 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2324 {
2325   GKeyFile *key_file;
2326   GError *load_error;
2327   gchar *filename, **mime_types;
2328   char *unaliased_type;
2329   char **desktop_file_ids;
2330   int i;
2331   struct stat buf;
2332
2333   load_error = NULL;
2334   mime_types = NULL;
2335
2336   if (dir->mimeapps_list_added_map != NULL &&
2337       !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2338                                         &dir->mimeapps_list_timestamp))
2339     return;
2340   
2341   if (dir->mimeapps_list_added_map != NULL)
2342     g_hash_table_destroy (dir->mimeapps_list_added_map);
2343   dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2344                                                         g_free, (GDestroyNotify)g_strfreev);
2345   
2346   if (dir->mimeapps_list_removed_map != NULL)
2347     g_hash_table_destroy (dir->mimeapps_list_removed_map);
2348   dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2349                                                           g_free, (GDestroyNotify)g_strfreev);
2350
2351   key_file = g_key_file_new ();
2352   
2353   filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2354   if (g_stat (filename, &buf) < 0)
2355     goto error;
2356
2357   if (dir->mimeapps_list_timestamp > 0) 
2358     mime_info_cache->should_ping_mime_monitor = TRUE;
2359
2360   dir->mimeapps_list_timestamp = buf.st_mtime;
2361
2362   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2363   g_free (filename);
2364   filename = NULL;
2365
2366   if (load_error != NULL)
2367     goto error;
2368
2369   mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2370                                     NULL, NULL);
2371   if (mime_types != NULL)
2372     {
2373       for (i = 0; mime_types[i] != NULL; i++)
2374         {
2375           desktop_file_ids = g_key_file_get_string_list (key_file,
2376                                                          ADDED_ASSOCIATIONS_GROUP,
2377                                                          mime_types[i],
2378                                                          NULL,
2379                                                          NULL);
2380           if (desktop_file_ids == NULL)
2381             continue;
2382           
2383           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2384           g_hash_table_replace (dir->mimeapps_list_added_map,
2385                                 unaliased_type,
2386                                 desktop_file_ids);
2387         }
2388       
2389       g_strfreev (mime_types);
2390     }
2391
2392   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2393                                     NULL, NULL);
2394   if (mime_types != NULL)
2395     {
2396       for (i = 0; mime_types[i] != NULL; i++)
2397         {
2398           desktop_file_ids = g_key_file_get_string_list (key_file,
2399                                                          REMOVED_ASSOCIATIONS_GROUP,
2400                                                          mime_types[i],
2401                                                          NULL,
2402                                                          NULL);
2403           if (desktop_file_ids == NULL)
2404             continue;
2405           
2406           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2407           g_hash_table_replace (dir->mimeapps_list_removed_map,
2408                                 unaliased_type,
2409                                 desktop_file_ids);
2410         }
2411       
2412       g_strfreev (mime_types);
2413     }
2414
2415   g_key_file_free (key_file);
2416   return;
2417   
2418  error:
2419   g_free (filename);
2420   g_key_file_free (key_file);
2421   
2422   if (mime_types != NULL)
2423     g_strfreev (mime_types);
2424   
2425   if (load_error)
2426     g_error_free (load_error);
2427 }
2428
2429 static MimeInfoCacheDir *
2430 mime_info_cache_dir_new (const char *path)
2431 {
2432   MimeInfoCacheDir *dir;
2433
2434   dir = g_new0 (MimeInfoCacheDir, 1);
2435   dir->path = g_strdup (path);
2436   
2437   return dir;
2438 }
2439
2440 static void
2441 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2442 {
2443   if (dir == NULL)
2444     return;
2445   
2446   if (dir->mime_info_cache_map != NULL)
2447     {
2448       destroy_info_cache_map (dir->mime_info_cache_map);
2449       dir->mime_info_cache_map = NULL;
2450       
2451   }
2452   
2453   if (dir->defaults_list_map != NULL)
2454     {
2455       g_hash_table_destroy (dir->defaults_list_map);
2456       dir->defaults_list_map = NULL;
2457     }
2458   
2459   if (dir->mimeapps_list_added_map != NULL)
2460     {
2461       g_hash_table_destroy (dir->mimeapps_list_added_map);
2462       dir->mimeapps_list_added_map = NULL;
2463     }
2464   
2465   if (dir->mimeapps_list_removed_map != NULL)
2466     {
2467       g_hash_table_destroy (dir->mimeapps_list_removed_map);
2468       dir->mimeapps_list_removed_map = NULL;
2469     }
2470   
2471   g_free (dir);
2472 }
2473
2474 static void
2475 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
2476                                          const char        *mime_type,
2477                                          char             **new_desktop_file_ids)
2478 {
2479   GList *desktop_file_ids;
2480   int i;
2481   
2482   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2483                                           mime_type);
2484   
2485   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2486     {
2487       if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2488         desktop_file_ids = g_list_append (desktop_file_ids,
2489                                           g_strdup (new_desktop_file_ids[i]));
2490     }
2491   
2492   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2493 }
2494
2495 static void
2496 mime_info_cache_init_dir_lists (void)
2497 {
2498   const char * const *dirs;
2499   int i;
2500   
2501   mime_info_cache = mime_info_cache_new ();
2502   
2503   dirs = get_applications_search_path ();
2504   
2505   for (i = 0; dirs[i] != NULL; i++)
2506     {
2507       MimeInfoCacheDir *dir;
2508       
2509       dir = mime_info_cache_dir_new (dirs[i]);
2510       
2511       if (dir != NULL)
2512         {
2513           mime_info_cache_dir_init (dir);
2514           mime_info_cache_dir_init_defaults_list (dir);
2515           mime_info_cache_dir_init_mimeapps_list (dir);
2516           
2517           mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2518         }
2519     }
2520 }
2521
2522 static void
2523 mime_info_cache_update_dir_lists (void)
2524 {
2525   GList *tmp;
2526   
2527   tmp = mime_info_cache->dirs;
2528   
2529   while (tmp != NULL)
2530     {
2531       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2532
2533       /* No need to do this if we had file monitors... */
2534       mime_info_cache_blow_global_cache ();
2535       mime_info_cache_dir_init (dir);
2536       mime_info_cache_dir_init_defaults_list (dir);
2537       mime_info_cache_dir_init_mimeapps_list (dir);
2538       
2539       tmp = tmp->next;
2540     }
2541 }
2542
2543 static void
2544 mime_info_cache_init (void)
2545 {
2546   G_LOCK (mime_info_cache);
2547   if (mime_info_cache == NULL)
2548     mime_info_cache_init_dir_lists ();
2549   else
2550     {
2551       time_t now;
2552       
2553       time (&now);
2554       if (now >= mime_info_cache->last_stat_time + 10)
2555         {
2556           mime_info_cache_update_dir_lists ();
2557           mime_info_cache->last_stat_time = now;
2558         }
2559     }
2560   
2561   if (mime_info_cache->should_ping_mime_monitor)
2562     {
2563       /* g_idle_add (emit_mime_changed, NULL); */
2564       mime_info_cache->should_ping_mime_monitor = FALSE;
2565     }
2566   
2567   G_UNLOCK (mime_info_cache);
2568 }
2569
2570 static MimeInfoCache *
2571 mime_info_cache_new (void)
2572 {
2573   MimeInfoCache *cache;
2574   
2575   cache = g_new0 (MimeInfoCache, 1);
2576   
2577   cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2578                                                         (GDestroyNotify) g_free,
2579                                                         (GDestroyNotify) g_free);
2580   return cache;
2581 }
2582
2583 static void
2584 mime_info_cache_free (MimeInfoCache *cache)
2585 {
2586   if (cache == NULL)
2587     return;
2588   
2589   g_list_foreach (cache->dirs,
2590                   (GFunc) mime_info_cache_dir_free,
2591                   NULL);
2592   g_list_free (cache->dirs);
2593   g_hash_table_destroy (cache->global_defaults_cache);
2594   g_free (cache);
2595 }
2596
2597 /**
2598  * mime_info_cache_reload:
2599  * @dir: directory path which needs reloading.
2600  * 
2601  * Reload the mime information for the @dir.
2602  */
2603 static void
2604 mime_info_cache_reload (const char *dir)
2605 {
2606   /* FIXME: just reload the dir that needs reloading,
2607    * don't blow the whole cache
2608    */
2609   if (mime_info_cache != NULL)
2610     {
2611       G_LOCK (mime_info_cache);
2612       mime_info_cache_free (mime_info_cache);
2613       mime_info_cache = NULL;
2614       G_UNLOCK (mime_info_cache);
2615     }
2616 }
2617
2618 static GList *
2619 append_desktop_entry (GList      *list, 
2620                       const char *desktop_entry,
2621                       GList      *removed_entries)
2622 {
2623   /* Add if not already in list, and valid */
2624   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2625       !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2626     list = g_list_prepend (list, g_strdup (desktop_entry));
2627   
2628   return list;
2629 }
2630
2631 /**
2632  * get_all_desktop_entries_for_mime_type:
2633  * @mime_type: a mime type.
2634  * @except: NULL or a strv list
2635  *
2636  * Returns all the desktop ids for @mime_type. The desktop files
2637  * are listed in an order so that default applications are listed before
2638  * non-default ones, and handlers for inherited mimetypes are listed
2639  * after the base ones.
2640  *
2641  * Optionally doesn't list the desktop ids given in the @except 
2642  *
2643  * Return value: a #GList containing the desktop ids which claim
2644  *    to handle @mime_type.
2645  */
2646 static GList *
2647 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2648                                        const char **except)
2649 {
2650   GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2651   MimeInfoCacheDir *dir;
2652   char *mime_type;
2653   char **mime_types;
2654   char **default_entries;
2655   char **removed_associations;
2656   int i, j, k;
2657   GPtrArray *array;
2658   char **anc;
2659   
2660   mime_info_cache_init ();
2661
2662   /* collect all ancestors */
2663   mime_types = _g_unix_content_type_get_parents (base_mime_type);
2664   array = g_ptr_array_new ();
2665   for (i = 0; mime_types[i]; i++)
2666     g_ptr_array_add (array, mime_types[i]);
2667   g_free (mime_types);
2668   for (i = 0; i < array->len; i++)
2669     {
2670       anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2671       for (j = 0; anc[j]; j++)
2672         {
2673           for (k = 0; k < array->len; k++)
2674             {
2675               if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2676                 break;
2677             }
2678           if (k == array->len) /* not found */
2679             g_ptr_array_add (array, g_strdup (anc[j]));
2680         }
2681       g_strfreev (anc);
2682     }
2683   g_ptr_array_add (array, NULL);
2684   mime_types = (char **)g_ptr_array_free (array, FALSE);
2685
2686   G_LOCK (mime_info_cache);
2687   
2688   removed_entries = NULL;
2689   desktop_entries = NULL;
2690
2691   for (i = 0; except != NULL && except[i] != NULL; i++)
2692     removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2693   
2694   for (i = 0; mime_types[i] != NULL; i++)
2695     {
2696       mime_type = mime_types[i];
2697
2698       /* Go through all apps listed as defaults */
2699       for (dir_list = mime_info_cache->dirs;
2700            dir_list != NULL;
2701            dir_list = dir_list->next)
2702         {
2703           dir = dir_list->data;
2704
2705           /* First added associations from mimeapps.list */
2706           default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2707           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2708             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2709
2710           /* Then removed associations from mimeapps.list */
2711           removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2712           for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2713             removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2714
2715           /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2716           default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2717           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2718             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2719         }
2720
2721       /* Go through all entries that support the mimetype */
2722       for (dir_list = mime_info_cache->dirs;
2723            dir_list != NULL;
2724            dir_list = dir_list->next) 
2725         {
2726           dir = dir_list->data;
2727         
2728           list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2729           for (tmp = list; tmp != NULL; tmp = tmp->next)
2730             desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2731         }
2732     }
2733   
2734   G_UNLOCK (mime_info_cache);
2735
2736   g_strfreev (mime_types);
2737
2738   g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2739   g_list_free (removed_entries);
2740   
2741   desktop_entries = g_list_reverse (desktop_entries);
2742   
2743   return desktop_entries;
2744 }
2745
2746 /* GDesktopAppInfoLookup interface: */
2747
2748 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2749 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2750                                                   gpointer class_data);
2751
2752 GType
2753 g_desktop_app_info_lookup_get_type (void)
2754 {
2755   static volatile gsize g_define_type_id__volatile = 0;
2756
2757   if (g_once_init_enter (&g_define_type_id__volatile))
2758     {
2759       const GTypeInfo desktop_app_info_lookup_info =
2760       {
2761         sizeof (GDesktopAppInfoLookupIface), /* class_size */
2762         g_desktop_app_info_lookup_base_init,   /* base_init */
2763         NULL,           /* base_finalize */
2764         g_desktop_app_info_lookup_class_init,
2765         NULL,           /* class_finalize */
2766         NULL,           /* class_data */
2767         0,
2768         0,              /* n_preallocs */
2769         NULL
2770       };
2771       GType g_define_type_id =
2772         g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2773                                 &desktop_app_info_lookup_info, 0);
2774
2775       g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2776
2777       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2778     }
2779
2780   return g_define_type_id__volatile;
2781 }
2782
2783 static void
2784 g_desktop_app_info_lookup_class_init (gpointer g_class,
2785                                       gpointer class_data)
2786 {
2787 }
2788
2789 static void
2790 g_desktop_app_info_lookup_base_init (gpointer g_class)
2791 {
2792 }
2793
2794 /**
2795  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2796  * @lookup: a #GDesktopAppInfoLookup
2797  * @uri_scheme: a string containing a URI scheme.
2798  *
2799  * Gets the default application for launching applications 
2800  * using this URI scheme for a particular GDesktopAppInfoLookup
2801  * implementation.
2802  *
2803  * The GDesktopAppInfoLookup interface and this function is used
2804  * to implement g_app_info_get_default_for_uri_scheme() backends
2805  * in a GIO module. There is no reason for applications to use it
2806  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2807  * 
2808  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2809  */
2810 GAppInfo *
2811 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2812                                                       const char            *uri_scheme)
2813 {
2814   GDesktopAppInfoLookupIface *iface;
2815   
2816   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2817
2818   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2819
2820   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2821 }
2822
2823 #define __G_DESKTOP_APP_INFO_C__
2824 #include "gioaliasdef.c"