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