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