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