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