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