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