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