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