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