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