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