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