Use G_DEFINE_INTERFACE in gio
[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 = g_string_new (NULL);
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   while (*p)
730     {
731       if (p[0] == '%' && p[1] != '\0')
732         {
733           expand_macro (p[1], expanded_exec, info, uris);
734           p++;
735         }
736       else
737         g_string_append_c (expanded_exec, *p);
738       
739       p++;
740     }
741   
742   /* No file substitutions */
743   if (uri_list == *uris && uri_list != NULL)
744     {
745       /* If there is no macro default to %f. This is also what KDE does */
746       g_string_append_c (expanded_exec, ' ');
747       expand_macro ('f', expanded_exec, info, uris);
748     }
749   
750   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
751   g_string_free (expanded_exec, TRUE);
752   return res;
753 }
754
755 static gboolean
756 prepend_terminal_to_vector (int    *argc,
757                             char ***argv)
758 {
759 #ifndef G_OS_WIN32
760   char **real_argv;
761   int real_argc;
762   int i, j;
763   char **term_argv = NULL;
764   int term_argc = 0;
765   char *check;
766   char **the_argv;
767   
768   g_return_val_if_fail (argc != NULL, FALSE);
769   g_return_val_if_fail (argv != NULL, FALSE);
770         
771   /* sanity */
772   if(*argv == NULL)
773     *argc = 0;
774   
775   the_argv = *argv;
776
777   /* compute size if not given */
778   if (*argc < 0)
779     {
780       for (i = 0; the_argv[i] != NULL; i++)
781         ;
782       *argc = i;
783     }
784   
785   term_argc = 2;
786   term_argv = g_new0 (char *, 3);
787
788   check = g_find_program_in_path ("gnome-terminal");
789   if (check != NULL)
790     {
791       term_argv[0] = check;
792       /* Note that gnome-terminal takes -x and
793        * as -e in gnome-terminal is broken we use that. */
794       term_argv[1] = g_strdup ("-x");
795     }
796   else
797     {
798       if (check == NULL)
799         check = g_find_program_in_path ("nxterm");
800       if (check == NULL)
801         check = g_find_program_in_path ("color-xterm");
802       if (check == NULL)
803         check = g_find_program_in_path ("rxvt");
804       if (check == NULL)
805         check = g_find_program_in_path ("xterm");
806       if (check == NULL)
807         check = g_find_program_in_path ("dtterm");
808       if (check == NULL)
809         {
810           check = g_strdup ("xterm");
811           g_warning ("couldn't find a terminal, falling back to xterm");
812         }
813       term_argv[0] = check;
814       term_argv[1] = g_strdup ("-e");
815     }
816
817   real_argc = term_argc + *argc;
818   real_argv = g_new (char *, real_argc + 1);
819   
820   for (i = 0; i < term_argc; i++)
821     real_argv[i] = term_argv[i];
822   
823   for (j = 0; j < *argc; j++, i++)
824     real_argv[i] = (char *)the_argv[j];
825   
826   real_argv[i] = NULL;
827   
828   g_free (*argv);
829   *argv = real_argv;
830   *argc = real_argc;
831   
832   /* we use g_free here as we sucked all the inner strings
833    * out from it into real_argv */
834   g_free (term_argv);
835   return TRUE;
836 #else
837   return FALSE;
838 #endif /* G_OS_WIN32 */
839 }
840
841 static GList *
842 uri_list_segment_to_files (GList *start,
843                            GList *end)
844 {
845   GList *res;
846   GFile *file;
847
848   res = NULL;
849   while (start != NULL && start != end)
850     {
851       file = g_file_new_for_uri ((char *)start->data);
852       res = g_list_prepend (res, file);
853       start = start->next;
854     }
855
856   return g_list_reverse (res);
857 }
858
859 typedef struct
860 {
861   char *display;
862   char *sn_id;
863 } ChildSetupData;
864
865 static void
866 child_setup (gpointer user_data)
867 {
868   ChildSetupData *data = user_data;
869
870   if (data->display)
871     g_setenv ("DISPLAY", data->display, TRUE);
872
873   if (data->sn_id)
874     g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
875 }
876
877 static gboolean
878 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
879                                 GList              *uris,
880                                 GAppLaunchContext  *launch_context,
881                                 GError            **error)
882 {
883   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
884   gboolean completed = FALSE;
885   GList *old_uris;
886   GList *launched_files;
887   char **argv;
888   int argc;
889   ChildSetupData data;
890
891   g_return_val_if_fail (appinfo != NULL, FALSE);
892
893   argv = NULL;
894
895   do
896     {
897       old_uris = uris;
898       if (!expand_application_parameters (info, &uris,
899                                           &argc, &argv, error))
900         goto out;
901       
902       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
903         {
904           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
905                                _("Unable to find terminal required for application"));
906           goto out;
907         }
908
909       data.display = NULL;
910       data.sn_id = NULL;
911
912       if (launch_context)
913         {
914           launched_files = uri_list_segment_to_files (old_uris, uris);
915
916           data.display = g_app_launch_context_get_display (launch_context,
917                                                            appinfo,
918                                                            launched_files);
919
920           if (info->startup_notify)
921             data.sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
922                                                                      appinfo,
923                                                                      launched_files);
924           g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
925           g_list_free (launched_files);
926         }
927
928       if (!g_spawn_async (info->path,
929                           argv,
930                           NULL,
931                           G_SPAWN_SEARCH_PATH,
932                           child_setup,
933                           &data,
934                           NULL,
935                           error))
936         {
937           if (data.sn_id)
938             g_app_launch_context_launch_failed (launch_context, data.sn_id);
939
940           g_free (data.sn_id);
941           g_free (data.display);
942
943           goto out;
944         }
945
946       g_free (data.sn_id);
947       g_free (data.display);
948
949       g_strfreev (argv);
950       argv = NULL;
951     }
952   while (uris != NULL);
953
954   completed = TRUE;
955
956  out:
957   g_strfreev (argv);
958
959   return completed;
960 }
961
962 static gboolean
963 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
964 {
965   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
966  
967   return info->exec && 
968     ((strstr (info->exec, "%u") != NULL) ||
969      (strstr (info->exec, "%U") != NULL));
970 }
971
972 static gboolean
973 g_desktop_app_info_supports_files (GAppInfo *appinfo)
974 {
975   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
976  
977   return info->exec && 
978     ((strstr (info->exec, "%f") != NULL) ||
979      (strstr (info->exec, "%F") != NULL));
980 }
981
982 static gboolean
983 g_desktop_app_info_launch (GAppInfo           *appinfo,
984                            GList              *files,
985                            GAppLaunchContext  *launch_context,
986                            GError            **error)
987 {
988   GList *uris;
989   char *uri;
990   gboolean res;
991
992   uris = NULL;
993   while (files)
994     {
995       uri = g_file_get_uri (files->data);
996       uris = g_list_prepend (uris, uri);
997       files = files->next;
998     }
999   
1000   uris = g_list_reverse (uris);
1001   
1002   res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1003   
1004   g_list_foreach  (uris, (GFunc)g_free, NULL);
1005   g_list_free (uris);
1006   
1007   return res;
1008 }
1009
1010 G_LOCK_DEFINE_STATIC (g_desktop_env);
1011 static gchar *g_desktop_env = NULL;
1012
1013 /**
1014  * g_desktop_app_info_set_desktop_env:
1015  * @desktop_env: a string specifying what desktop this is
1016  *
1017  * Sets the name of the desktop that the application is running in.
1018  * This is used by g_app_info_should_show() to evaluate the
1019  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1020  * desktop entry fields.
1021  *
1022  * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop 
1023  * Menu specification</ulink> recognizes the following:
1024  * <simplelist>
1025  *   <member>GNOME</member>
1026  *   <member>KDE</member>
1027  *   <member>ROX</member>
1028  *   <member>XFCE</member>
1029  *   <member>Old</member> 
1030  * </simplelist>
1031  *
1032  * Should be called only once; subsequent calls are ignored.
1033  */
1034 void
1035 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1036 {
1037   G_LOCK (g_desktop_env);
1038   if (!g_desktop_env)
1039     g_desktop_env = g_strdup (desktop_env);
1040   G_UNLOCK (g_desktop_env);
1041 }
1042
1043 static gboolean
1044 g_desktop_app_info_should_show (GAppInfo *appinfo)
1045 {
1046   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1047   gboolean found;
1048   const gchar *desktop_env;
1049   int i;
1050
1051   if (info->nodisplay)
1052     return FALSE;
1053
1054   G_LOCK (g_desktop_env);
1055   desktop_env = g_desktop_env;
1056   G_UNLOCK (g_desktop_env);
1057
1058   if (info->only_show_in)
1059     {
1060       if (desktop_env == NULL)
1061         return FALSE;
1062       
1063       found = FALSE;
1064       for (i = 0; info->only_show_in[i] != NULL; i++)
1065         {
1066           if (strcmp (info->only_show_in[i], desktop_env) == 0)
1067             {
1068               found = TRUE;
1069               break;
1070             }
1071         }
1072       if (!found)
1073         return FALSE;
1074     }
1075
1076   if (info->not_show_in && desktop_env)
1077     {
1078       for (i = 0; info->not_show_in[i] != NULL; i++)
1079         {
1080           if (strcmp (info->not_show_in[i], desktop_env) == 0)
1081             return FALSE;
1082         }
1083     }
1084   
1085   return TRUE;
1086 }
1087
1088 typedef enum {
1089   APP_DIR,
1090   MIMETYPE_DIR
1091 } DirType;
1092
1093 static char *
1094 ensure_dir (DirType   type,
1095             GError  **error)
1096 {
1097   char *path, *display_name;
1098   int errsv;
1099
1100   if (type == APP_DIR)
1101     path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1102   else
1103     path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1104
1105   errno = 0;
1106   if (g_mkdir_with_parents (path, 0700) == 0)
1107     return path;
1108
1109   errsv = errno;
1110   display_name = g_filename_display_name (path);
1111   if (type == APP_DIR)
1112     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1113                  _("Can't create user application configuration folder %s: %s"),
1114                  display_name, g_strerror (errsv));
1115   else
1116     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1117                  _("Can't create user MIME configuration folder %s: %s"),
1118                  display_name, g_strerror (errsv));
1119
1120   g_free (display_name);
1121   g_free (path);
1122
1123   return NULL;
1124 }
1125
1126 static gboolean
1127 update_mimeapps_list (const char  *desktop_id, 
1128                       const char  *content_type, 
1129                       gboolean     add_as_default,
1130                       gboolean     add_non_default,
1131                       gboolean     remove, 
1132                       GError     **error)
1133 {
1134   char *dirname, *filename;
1135   GKeyFile *key_file;
1136   gboolean load_succeeded, res;
1137   char **old_list, **list;
1138   GList *system_list, *l;
1139   gsize length, data_size;
1140   char *data;
1141   int i, j, k;
1142   char **content_types;
1143
1144   /* Don't add both at start and end */
1145   g_assert (!(add_as_default && add_non_default));
1146   
1147   dirname = ensure_dir (APP_DIR, error);
1148   if (!dirname)
1149     return FALSE;
1150
1151   filename = g_build_filename (dirname, "mimeapps.list", NULL);
1152   g_free (dirname);
1153
1154   key_file = g_key_file_new ();
1155   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1156   if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1157     {
1158       g_key_file_free (key_file);
1159       key_file = g_key_file_new ();
1160     }
1161
1162   if (content_type)
1163     {
1164       content_types = g_new (char *, 2);
1165       content_types[0] = g_strdup (content_type);
1166       content_types[1] = NULL;
1167     }
1168   else
1169     {
1170       content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1171     }
1172
1173   for (k = 0; content_types && content_types[k]; k++)
1174     { 
1175       /* Add to the right place in the list */
1176   
1177       length = 0;
1178       old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1179                                              content_types[k], &length, NULL);
1180
1181       list = g_new (char *, 1 + length + 1);
1182
1183       i = 0;
1184       if (add_as_default)
1185         list[i++] = g_strdup (desktop_id);
1186       if (old_list)
1187         {
1188           for (j = 0; old_list[j] != NULL; j++)
1189             {
1190               if (g_strcmp0 (old_list[j], desktop_id) != 0)
1191                 list[i++] = g_strdup (old_list[j]);
1192               else if (add_non_default)
1193                 {
1194                   /* If adding as non-default, and it's already in,
1195                      don't change order of desktop ids */
1196                   add_non_default = FALSE;
1197                   list[i++] = g_strdup (old_list[j]);
1198                 }
1199             }
1200         }
1201       
1202       if (add_non_default)
1203         {
1204           /* We're adding as non-default, and it wasn't already in the list,
1205              so we add at the end. But to avoid listing the app before the
1206              current system default (thus changing the default) we have to
1207              add the current list of (not yet listed) apps before it. */
1208
1209           list[i] = NULL; /* Terminate current list so we can use it */
1210           system_list =  get_all_desktop_entries_for_mime_type (content_type, (const char **)list);
1211           
1212           list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1213           
1214           for (l = system_list; l != NULL; l = l->next)
1215             {
1216               list[i++] = l->data; /* no strdup, taking ownership */
1217               if (g_strcmp0 (l->data, desktop_id) == 0)
1218                 add_non_default = FALSE;
1219             }
1220           g_list_free (system_list);
1221                   
1222           if (add_non_default)
1223             list[i++] = g_strdup (desktop_id);
1224         }
1225       
1226       list[i] = NULL;
1227   
1228       g_strfreev (old_list);
1229
1230       if (list[0] == NULL || desktop_id == NULL)
1231         g_key_file_remove_key (key_file,
1232                                ADDED_ASSOCIATIONS_GROUP,
1233                                content_types[k],
1234                                NULL);
1235       else
1236         g_key_file_set_string_list (key_file,
1237                                     ADDED_ASSOCIATIONS_GROUP,
1238                                     content_types[k],
1239                                     (const char * const *)list, i);
1240    
1241       g_strfreev (list);
1242     }
1243   
1244   if (content_type)
1245     {
1246       /* reuse the list from above */
1247     }
1248   else
1249     {
1250       g_strfreev (content_types);
1251       content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1252     }
1253
1254   for (k = 0; content_types && content_types[k]; k++) 
1255     {
1256       /* Remove from removed associations group (unless remove) */
1257   
1258       length = 0;
1259       old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1260                                              content_types[k], &length, NULL);
1261
1262       list = g_new (char *, 1 + length + 1);
1263
1264       i = 0;
1265       if (remove)
1266         list[i++] = g_strdup (desktop_id);
1267       if (old_list)
1268         {
1269           for (j = 0; old_list[j] != NULL; j++)
1270             {
1271               if (g_strcmp0 (old_list[j], desktop_id) != 0)
1272                 list[i++] = g_strdup (old_list[j]);
1273             }
1274         }
1275       list[i] = NULL;
1276   
1277       g_strfreev (old_list);
1278
1279       if (list[0] == NULL || desktop_id == NULL)
1280         g_key_file_remove_key (key_file,
1281                                REMOVED_ASSOCIATIONS_GROUP,
1282                                content_types[k],
1283                                NULL);
1284       else
1285         g_key_file_set_string_list (key_file,
1286                                     REMOVED_ASSOCIATIONS_GROUP,
1287                                     content_types[k],
1288                                     (const char * const *)list, i);
1289
1290       g_strfreev (list);
1291     }
1292   
1293   g_strfreev (content_types);  
1294
1295   data = g_key_file_to_data (key_file, &data_size, error);
1296   g_key_file_free (key_file);
1297   
1298   res = g_file_set_contents (filename, data, data_size, error);
1299
1300   mime_info_cache_reload (NULL);
1301                           
1302   g_free (filename);
1303   g_free (data);
1304   
1305   return res;
1306 }
1307
1308 static gboolean
1309 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
1310                                             const char  *content_type,
1311                                             GError     **error)
1312 {
1313   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1314
1315   if (!g_desktop_app_info_ensure_saved (info, error))
1316     return FALSE;  
1317   
1318   return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1319 }
1320
1321 static void
1322 update_program_done (GPid     pid,
1323                      gint     status,
1324                      gpointer data)
1325 {
1326   /* Did the application exit correctly */
1327   if (WIFEXITED (status) &&
1328       WEXITSTATUS (status) == 0)
1329     {
1330       /* Here we could clean out any caches in use */
1331     }
1332 }
1333
1334 static void
1335 run_update_command (char *command,
1336                     char *subdir)
1337 {
1338         char *argv[3] = {
1339                 NULL,
1340                 NULL,
1341                 NULL,
1342         };
1343         GPid pid = 0;
1344         GError *error = NULL;
1345
1346         argv[0] = command;
1347         argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1348
1349         if (g_spawn_async ("/", argv,
1350                            NULL,       /* envp */
1351                            G_SPAWN_SEARCH_PATH |
1352                            G_SPAWN_STDOUT_TO_DEV_NULL |
1353                            G_SPAWN_STDERR_TO_DEV_NULL |
1354                            G_SPAWN_DO_NOT_REAP_CHILD,
1355                            NULL, NULL, /* No setup function */
1356                            &pid,
1357                            &error)) 
1358           g_child_watch_add (pid, update_program_done, NULL);
1359         else
1360           {
1361             /* If we get an error at this point, it's quite likely the user doesn't
1362              * have an installed copy of either 'update-mime-database' or
1363              * 'update-desktop-database'.  I don't think we want to popup an error
1364              * dialog at this point, so we just do a g_warning to give the user a
1365              * chance of debugging it.
1366              */
1367             g_warning ("%s", error->message);
1368           }
1369         
1370         g_free (argv[1]);
1371 }
1372
1373 static gboolean
1374 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
1375                                                  const char  *extension,
1376                                                  GError     **error)
1377 {
1378   char *filename, *basename, *mimetype;
1379   char *dirname;
1380   gboolean res;
1381
1382   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1383     return FALSE;  
1384   
1385   dirname = ensure_dir (MIMETYPE_DIR, error);
1386   if (!dirname)
1387     return FALSE;
1388   
1389   basename = g_strdup_printf ("user-extension-%s.xml", extension);
1390   filename = g_build_filename (dirname, basename, NULL);
1391   g_free (basename);
1392   g_free (dirname);
1393
1394   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1395   
1396   if (!g_file_test (filename, G_FILE_TEST_EXISTS)) 
1397     {
1398       char *contents;
1399
1400       contents =
1401         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1402                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1403                          " <mime-type type=\"%s\">\n"
1404                          "  <comment>%s document</comment>\n"
1405                          "  <glob pattern=\"*.%s\"/>\n"
1406                          " </mime-type>\n"
1407                          "</mime-info>\n", mimetype, extension, extension);
1408
1409       g_file_set_contents (filename, contents, -1, NULL);
1410       g_free (contents);
1411
1412       run_update_command ("update-mime-database", "mime");
1413     }
1414   g_free (filename);
1415   
1416   res = g_desktop_app_info_set_as_default_for_type (appinfo,
1417                                                     mimetype,
1418                                                     error);
1419
1420   g_free (mimetype);
1421   
1422   return res;
1423 }
1424
1425 static gboolean
1426 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
1427                                       const char  *content_type,
1428                                       GError     **error)
1429 {
1430   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1431
1432   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1433     return FALSE;  
1434   
1435   return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1436 }
1437
1438 static gboolean
1439 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1440 {
1441   return TRUE;
1442 }
1443
1444 static gboolean
1445 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
1446                                          const char  *content_type,
1447                                          GError     **error)
1448 {
1449   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1450
1451   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1452     return FALSE;
1453   
1454   return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1455 }
1456
1457 static gboolean
1458 g_desktop_app_info_ensure_saved (GDesktopAppInfo  *info,
1459                                  GError          **error)
1460 {
1461   GKeyFile *key_file;
1462   char *dirname;
1463   char *filename;
1464   char *data, *desktop_id;
1465   gsize data_size;
1466   int fd;
1467   gboolean res;
1468   
1469   if (info->filename != NULL)
1470     return TRUE;
1471
1472   /* This is only used for object created with
1473    * g_app_info_create_from_commandline. All other
1474    * object should have a filename
1475    */
1476   
1477   dirname = ensure_dir (APP_DIR, error);
1478   if (!dirname)
1479     return FALSE;
1480   
1481   key_file = g_key_file_new ();
1482
1483   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1484                          "Encoding", "UTF-8");
1485   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1486                          G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1487   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1488                          G_KEY_FILE_DESKTOP_KEY_TYPE,
1489                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1490   if (info->terminal) 
1491     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1492                             G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1493
1494   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1495                          G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1496
1497   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1498                          G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1499
1500   if (info->fullname != NULL)
1501     g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1502                            FULL_NAME_KEY, info->fullname);
1503
1504   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1505                          G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1506   
1507   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1508                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1509
1510   data = g_key_file_to_data (key_file, &data_size, NULL);
1511   g_key_file_free (key_file);
1512
1513   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1514   filename = g_build_filename (dirname, desktop_id, NULL);
1515   g_free (desktop_id);
1516   g_free (dirname);
1517   
1518   fd = g_mkstemp (filename);
1519   if (fd == -1)
1520     {
1521       char *display_name;
1522
1523       display_name = g_filename_display_name (filename);
1524       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1525                    _("Can't create user desktop file %s"), display_name);
1526       g_free (display_name);
1527       g_free (filename);
1528       g_free (data);
1529       return FALSE;
1530     }
1531
1532   desktop_id = g_path_get_basename (filename);
1533
1534   close (fd);
1535   
1536   res = g_file_set_contents (filename, data, data_size, error);
1537   if (!res)
1538     {
1539       g_free (desktop_id);
1540       g_free (filename);
1541       return FALSE;
1542     }
1543
1544   info->filename = filename;
1545   info->desktop_id = desktop_id;
1546   
1547   run_update_command ("update-desktop-database", "applications");
1548   
1549   return TRUE;
1550 }
1551
1552 static gboolean
1553 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1554 {
1555   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1556
1557   if (info->filename)
1558     {
1559       if (strstr (info->filename, "/userapp-"))
1560         return g_access (info->filename, W_OK) == 0;
1561     }
1562
1563   return FALSE;
1564 }
1565
1566 static gboolean
1567 g_desktop_app_info_delete (GAppInfo *appinfo)
1568 {
1569   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1570   
1571   if (info->filename)
1572     { 
1573       if (g_remove (info->filename) == 0)
1574         {
1575           update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1576
1577           g_free (info->filename);
1578           info->filename = NULL;
1579           g_free (info->desktop_id);
1580           info->desktop_id = NULL;
1581
1582           return TRUE;
1583         }
1584     }
1585
1586   return FALSE;
1587 }
1588
1589 /**
1590  * g_app_info_create_from_commandline:
1591  * @commandline: the commandline to use
1592  * @application_name: the application name, or %NULL to use @commandline
1593  * @flags: flags that can specify details of the created #GAppInfo
1594  * @error: a #GError location to store the error occuring, %NULL to ignore.
1595  *
1596  * Creates a new #GAppInfo from the given information.
1597  *
1598  * Returns: new #GAppInfo for given command.
1599  **/
1600 GAppInfo *
1601 g_app_info_create_from_commandline (const char           *commandline,
1602                                     const char           *application_name,
1603                                     GAppInfoCreateFlags   flags,
1604                                     GError              **error)
1605 {
1606   char **split;
1607   char *basename;
1608   GDesktopAppInfo *info;
1609
1610   g_return_val_if_fail (commandline, NULL);
1611
1612   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1613
1614   info->filename = NULL;
1615   info->desktop_id = NULL;
1616   
1617   info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1618   info->startup_notify = FALSE;
1619   info->hidden = FALSE;
1620   if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1621     info->exec = g_strconcat (commandline, " %u", NULL);
1622   else
1623     info->exec = g_strconcat (commandline, " %f", NULL);
1624   info->nodisplay = TRUE;
1625   info->binary = binary_from_exec (info->exec);
1626   
1627   if (application_name)
1628     info->name = g_strdup (application_name);
1629   else
1630     {
1631       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1632       split = g_strsplit (commandline, " ", 2);
1633       basename = split[0] ? g_path_get_basename (split[0]) : NULL;
1634       g_strfreev (split);
1635       info->name = basename;
1636       if (info->name == NULL)
1637         info->name = g_strdup ("custom");
1638     }
1639   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1640   
1641   return G_APP_INFO (info);
1642 }
1643
1644 static void
1645 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1646 {
1647   iface->dup = g_desktop_app_info_dup;
1648   iface->equal = g_desktop_app_info_equal;
1649   iface->get_id = g_desktop_app_info_get_id;
1650   iface->get_name = g_desktop_app_info_get_name;
1651   iface->get_description = g_desktop_app_info_get_description;
1652   iface->get_executable = g_desktop_app_info_get_executable;
1653   iface->get_icon = g_desktop_app_info_get_icon;
1654   iface->launch = g_desktop_app_info_launch;
1655   iface->supports_uris = g_desktop_app_info_supports_uris;
1656   iface->supports_files = g_desktop_app_info_supports_files;
1657   iface->launch_uris = g_desktop_app_info_launch_uris;
1658   iface->should_show = g_desktop_app_info_should_show;
1659   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1660   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1661   iface->add_supports_type = g_desktop_app_info_add_supports_type;
1662   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1663   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1664   iface->can_delete = g_desktop_app_info_can_delete;
1665   iface->do_delete = g_desktop_app_info_delete;
1666   iface->get_commandline = g_desktop_app_info_get_commandline;
1667   iface->get_display_name = g_desktop_app_info_get_display_name;
1668 }
1669
1670 static gboolean
1671 app_info_in_list (GAppInfo *info, 
1672                   GList    *list)
1673 {
1674   while (list != NULL)
1675     {
1676       if (g_app_info_equal (info, list->data))
1677         return TRUE;
1678       list = list->next;
1679     }
1680   return FALSE;
1681 }
1682
1683
1684 /**
1685  * g_app_info_get_all_for_type:
1686  * @content_type: the content type to find a #GAppInfo for
1687  * 
1688  * Gets a list of all #GAppInfo<!-- -->s for a given content type.
1689  *
1690  * Returns: #GList of #GAppInfo<!-- -->s for given @content_type
1691  *    or %NULL on error.
1692  **/
1693 GList *
1694 g_app_info_get_all_for_type (const char *content_type)
1695 {
1696   GList *desktop_entries, *l;
1697   GList *infos;
1698   GDesktopAppInfo *info;
1699
1700   g_return_val_if_fail (content_type != NULL, NULL);
1701   
1702   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1703
1704   infos = NULL;
1705   for (l = desktop_entries; l != NULL; l = l->next)
1706     {
1707       char *desktop_entry = l->data;
1708
1709       info = g_desktop_app_info_new (desktop_entry);
1710       if (info)
1711         {
1712           if (app_info_in_list (G_APP_INFO (info), infos))
1713             g_object_unref (info);
1714           else
1715             infos = g_list_prepend (infos, info);
1716         }
1717       g_free (desktop_entry);
1718     }
1719
1720   g_list_free (desktop_entries);
1721   
1722   return g_list_reverse (infos);
1723 }
1724
1725 /**
1726  * g_app_info_reset_type_associations:
1727  * @content_type: a content type 
1728  *
1729  * Removes all changes to the type associations done by
1730  * g_app_info_set_as_default_for_type(), 
1731  * g_app_info_set_as_default_for_extension(), 
1732  * g_app_info_add_supports_type() or g_app_info_remove_supports_type().
1733  *
1734  * Since: 2.20
1735  */
1736 void
1737 g_app_info_reset_type_associations (const char *content_type)
1738 {
1739   update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1740 }
1741
1742 /**
1743  * g_app_info_get_default_for_type:
1744  * @content_type: the content type to find a #GAppInfo for
1745  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1746  *     support URIs
1747  * 
1748  * Gets the #GAppInfo that corresponds to a given content type.
1749  *
1750  * Returns: #GAppInfo for given @content_type or %NULL on error.
1751  **/
1752 GAppInfo *
1753 g_app_info_get_default_for_type (const char *content_type,
1754                                  gboolean    must_support_uris)
1755 {
1756   GList *desktop_entries, *l;
1757   GAppInfo *info;
1758
1759   g_return_val_if_fail (content_type != NULL, NULL);
1760   
1761   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1762
1763   info = NULL;
1764   for (l = desktop_entries; l != NULL; l = l->next)
1765     {
1766       char *desktop_entry = l->data;
1767
1768       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1769       if (info)
1770         {
1771           if (must_support_uris && !g_app_info_supports_uris (info))
1772             {
1773               g_object_unref (info);
1774               info = NULL;
1775             }
1776           else
1777             break;
1778         }
1779     }
1780   
1781   g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
1782   g_list_free (desktop_entries);
1783   
1784   return info;
1785 }
1786
1787 /**
1788  * g_app_info_get_default_for_uri_scheme:
1789  * @uri_scheme: a string containing a URI scheme.
1790  *
1791  * Gets the default application for launching applications 
1792  * using this URI scheme. A URI scheme is the initial part 
1793  * of the URI, up to but not including the ':', e.g. "http", 
1794  * "ftp" or "sip".
1795  * 
1796  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1797  **/
1798 GAppInfo *
1799 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1800 {
1801   static gsize lookup = 0;
1802   
1803   if (g_once_init_enter (&lookup))
1804     {
1805       gsize setup_value = 1;
1806       GDesktopAppInfoLookup *lookup_instance;
1807       const char *use_this;
1808       GIOExtensionPoint *ep;
1809       GIOExtension *extension;
1810       GList *l;
1811
1812       use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1813       
1814       /* Ensure vfs in modules loaded */
1815       _g_io_modules_ensure_loaded ();
1816       
1817       ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1818
1819       lookup_instance = NULL;
1820       if (use_this)
1821         {
1822           extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1823           if (extension)
1824             lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1825         }
1826       
1827       if (lookup_instance == NULL)
1828         {
1829           for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1830             {
1831               extension = l->data;
1832               lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1833               if (lookup_instance != NULL)
1834                 break;
1835             }
1836         }
1837
1838       if (lookup_instance != NULL)
1839         setup_value = (gsize)lookup_instance;
1840       
1841       g_once_init_leave (&lookup, setup_value);
1842     }
1843
1844   if (lookup == 1)
1845     return NULL;
1846
1847   return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1848                                                                uri_scheme);
1849 }
1850
1851
1852 static void
1853 get_apps_from_dir (GHashTable *apps, 
1854                    const char *dirname, 
1855                    const char *prefix)
1856 {
1857   GDir *dir;
1858   const char *basename;
1859   char *filename, *subprefix, *desktop_id;
1860   gboolean hidden;
1861   GDesktopAppInfo *appinfo;
1862   
1863   dir = g_dir_open (dirname, 0, NULL);
1864   if (dir)
1865     {
1866       while ((basename = g_dir_read_name (dir)) != NULL)
1867         {
1868           filename = g_build_filename (dirname, basename, NULL);
1869           if (g_str_has_suffix (basename, ".desktop"))
1870             {
1871               desktop_id = g_strconcat (prefix, basename, NULL);
1872
1873               /* Use _extended so we catch NULLs too (hidden) */
1874               if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1875                 {
1876                   appinfo = g_desktop_app_info_new_from_filename (filename);
1877                   hidden = FALSE;
1878
1879                   if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1880                     {
1881                       g_object_unref (appinfo);
1882                       appinfo = NULL;
1883                       hidden = TRUE;
1884                     }
1885                                       
1886                   if (appinfo || hidden)
1887                     {
1888                       g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1889
1890                       if (appinfo)
1891                         {
1892                           /* Reuse instead of strdup here */
1893                           appinfo->desktop_id = desktop_id;
1894                           desktop_id = NULL;
1895                         }
1896                     }
1897                 }
1898               g_free (desktop_id);
1899             }
1900           else
1901             {
1902               if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1903                 {
1904                   subprefix = g_strconcat (prefix, basename, "-", NULL);
1905                   get_apps_from_dir (apps, filename, subprefix);
1906                   g_free (subprefix);
1907                 }
1908             }
1909           g_free (filename);
1910         }
1911       g_dir_close (dir);
1912     }
1913 }
1914
1915
1916 /**
1917  * g_app_info_get_all:
1918  *
1919  * Gets a list of all of the applications currently registered 
1920  * on this system.
1921  * 
1922  * For desktop files, this includes applications that have 
1923  * <literal>NoDisplay=true</literal> set or are excluded from 
1924  * display by means of <literal>OnlyShowIn</literal> or
1925  * <literal>NotShowIn</literal>. See g_app_info_should_show().
1926  * The returned list does not include applications which have
1927  * the <literal>Hidden</literal> key set. 
1928  * 
1929  * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
1930  **/
1931 GList *
1932 g_app_info_get_all (void)
1933 {
1934   const char * const *dirs;
1935   GHashTable *apps;
1936   GHashTableIter iter;
1937   gpointer value;
1938   int i;
1939   GList *infos;
1940
1941   dirs = get_applications_search_path ();
1942
1943   apps = g_hash_table_new_full (g_str_hash, g_str_equal,
1944                                 g_free, NULL);
1945
1946   
1947   for (i = 0; dirs[i] != NULL; i++)
1948     get_apps_from_dir (apps, dirs[i], "");
1949
1950
1951   infos = NULL;
1952   g_hash_table_iter_init (&iter, apps);
1953   while (g_hash_table_iter_next (&iter, NULL, &value))
1954     {
1955       if (value)
1956         infos = g_list_prepend (infos, value);
1957     }
1958
1959   g_hash_table_destroy (apps);
1960
1961   return g_list_reverse (infos);
1962 }
1963
1964 /* Cacheing of mimeinfo.cache and defaults.list files */
1965
1966 typedef struct {
1967   char *path;
1968   GHashTable *mime_info_cache_map;
1969   GHashTable *defaults_list_map;
1970   GHashTable *mimeapps_list_added_map;
1971   GHashTable *mimeapps_list_removed_map;
1972   time_t mime_info_cache_timestamp;
1973   time_t defaults_list_timestamp;
1974   time_t mimeapps_list_timestamp;
1975 } MimeInfoCacheDir;
1976
1977 typedef struct {
1978   GList *dirs;                       /* mimeinfo.cache and defaults.list */
1979   GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
1980   time_t last_stat_time;
1981   guint should_ping_mime_monitor : 1;
1982 } MimeInfoCache;
1983
1984 static MimeInfoCache *mime_info_cache = NULL;
1985 G_LOCK_DEFINE_STATIC (mime_info_cache);
1986
1987 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
1988                                                      const char        *mime_type,
1989                                                      char             **new_desktop_file_ids);
1990
1991 static MimeInfoCache * mime_info_cache_new (void);
1992
1993 static void
1994 destroy_info_cache_value (gpointer  key, 
1995                           GList    *value, 
1996                           gpointer  data)
1997 {
1998   g_list_foreach (value, (GFunc)g_free, NULL);
1999   g_list_free (value);
2000 }
2001
2002 static void
2003 destroy_info_cache_map (GHashTable *info_cache_map)
2004 {
2005   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2006   g_hash_table_destroy (info_cache_map);
2007 }
2008
2009 static gboolean
2010 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2011                                  const char       *cache_file,
2012                                  time_t           *timestamp)
2013 {
2014   struct stat buf;
2015   char *filename;
2016   
2017   filename = g_build_filename (dir->path, cache_file, NULL);
2018   
2019   if (g_stat (filename, &buf) < 0)
2020     {
2021       g_free (filename);
2022       return TRUE;
2023     }
2024   g_free (filename);
2025
2026   if (buf.st_mtime != *timestamp) 
2027     return TRUE;
2028   
2029   return FALSE;
2030 }
2031
2032 /* Call with lock held */
2033 static gboolean
2034 remove_all (gpointer  key,
2035             gpointer  value,
2036             gpointer  user_data)
2037 {
2038   return TRUE;
2039 }
2040
2041
2042 static void
2043 mime_info_cache_blow_global_cache (void)
2044 {
2045   g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2046                                remove_all, NULL);
2047 }
2048
2049 static void
2050 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2051 {
2052   GError *load_error;
2053   GKeyFile *key_file;
2054   gchar *filename, **mime_types;
2055   int i;
2056   struct stat buf;
2057   
2058   load_error = NULL;
2059   mime_types = NULL;
2060   
2061   if (dir->mime_info_cache_map != NULL &&
2062       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2063                                         &dir->mime_info_cache_timestamp))
2064     return;
2065   
2066   if (dir->mime_info_cache_map != NULL)
2067     destroy_info_cache_map (dir->mime_info_cache_map);
2068   
2069   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2070                                                     (GDestroyNotify) g_free,
2071                                                     NULL);
2072   
2073   key_file = g_key_file_new ();
2074   
2075   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2076   
2077   if (g_stat (filename, &buf) < 0)
2078     goto error;
2079   
2080   if (dir->mime_info_cache_timestamp > 0) 
2081     mime_info_cache->should_ping_mime_monitor = TRUE;
2082   
2083   dir->mime_info_cache_timestamp = buf.st_mtime;
2084   
2085   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2086   
2087   g_free (filename);
2088   filename = NULL;
2089   
2090   if (load_error != NULL)
2091     goto error;
2092   
2093   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2094                                     NULL, &load_error);
2095   
2096   if (load_error != NULL)
2097     goto error;
2098   
2099   for (i = 0; mime_types[i] != NULL; i++)
2100     {
2101       gchar **desktop_file_ids;
2102       char *unaliased_type;
2103       desktop_file_ids = g_key_file_get_string_list (key_file,
2104                                                      MIME_CACHE_GROUP,
2105                                                      mime_types[i],
2106                                                      NULL,
2107                                                      NULL);
2108       
2109       if (desktop_file_ids == NULL)
2110         continue;
2111
2112       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2113       mime_info_cache_dir_add_desktop_entries (dir,
2114                                                unaliased_type,
2115                                                desktop_file_ids);
2116       g_free (unaliased_type);
2117     
2118       g_strfreev (desktop_file_ids);
2119     }
2120   
2121   g_strfreev (mime_types);
2122   g_key_file_free (key_file);
2123   
2124   return;
2125  error:
2126   g_free (filename);
2127   g_key_file_free (key_file);
2128   
2129   if (mime_types != NULL)
2130     g_strfreev (mime_types);
2131   
2132   if (load_error)
2133     g_error_free (load_error);
2134 }
2135
2136 static void
2137 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2138 {
2139   GKeyFile *key_file;
2140   GError *load_error;
2141   gchar *filename, **mime_types;
2142   char *unaliased_type;
2143   char **desktop_file_ids;
2144   int i;
2145   struct stat buf;
2146
2147   load_error = NULL;
2148   mime_types = NULL;
2149
2150   if (dir->defaults_list_map != NULL &&
2151       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2152                                         &dir->defaults_list_timestamp))
2153     return;
2154   
2155   if (dir->defaults_list_map != NULL)
2156     g_hash_table_destroy (dir->defaults_list_map);
2157   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2158                                                   g_free, (GDestroyNotify)g_strfreev);
2159   
2160
2161   key_file = g_key_file_new ();
2162   
2163   filename = g_build_filename (dir->path, "defaults.list", NULL);
2164   if (g_stat (filename, &buf) < 0)
2165     goto error;
2166
2167   if (dir->defaults_list_timestamp > 0) 
2168     mime_info_cache->should_ping_mime_monitor = TRUE;
2169
2170   dir->defaults_list_timestamp = buf.st_mtime;
2171
2172   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2173   g_free (filename);
2174   filename = NULL;
2175
2176   if (load_error != NULL)
2177     goto error;
2178
2179   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2180                                     NULL, NULL);
2181   if (mime_types != NULL)
2182     {
2183       for (i = 0; mime_types[i] != NULL; i++)
2184         {
2185           desktop_file_ids = g_key_file_get_string_list (key_file,
2186                                                          DEFAULT_APPLICATIONS_GROUP,
2187                                                          mime_types[i],
2188                                                          NULL,
2189                                                          NULL);
2190           if (desktop_file_ids == NULL)
2191             continue;
2192           
2193           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2194           g_hash_table_replace (dir->defaults_list_map,
2195                                 unaliased_type,
2196                                 desktop_file_ids);
2197         }
2198       
2199       g_strfreev (mime_types);
2200     }
2201
2202   g_key_file_free (key_file);
2203   return;
2204   
2205  error:
2206   g_free (filename);
2207   g_key_file_free (key_file);
2208   
2209   if (mime_types != NULL)
2210     g_strfreev (mime_types);
2211   
2212   if (load_error)
2213     g_error_free (load_error);
2214 }
2215
2216 static void
2217 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2218 {
2219   GKeyFile *key_file;
2220   GError *load_error;
2221   gchar *filename, **mime_types;
2222   char *unaliased_type;
2223   char **desktop_file_ids;
2224   int i;
2225   struct stat buf;
2226
2227   load_error = NULL;
2228   mime_types = NULL;
2229
2230   if (dir->mimeapps_list_added_map != NULL &&
2231       !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2232                                         &dir->mimeapps_list_timestamp))
2233     return;
2234   
2235   if (dir->mimeapps_list_added_map != NULL)
2236     g_hash_table_destroy (dir->mimeapps_list_added_map);
2237   dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2238                                                         g_free, (GDestroyNotify)g_strfreev);
2239   
2240   if (dir->mimeapps_list_removed_map != NULL)
2241     g_hash_table_destroy (dir->mimeapps_list_removed_map);
2242   dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2243                                                           g_free, (GDestroyNotify)g_strfreev);
2244
2245   key_file = g_key_file_new ();
2246   
2247   filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2248   if (g_stat (filename, &buf) < 0)
2249     goto error;
2250
2251   if (dir->mimeapps_list_timestamp > 0) 
2252     mime_info_cache->should_ping_mime_monitor = TRUE;
2253
2254   dir->mimeapps_list_timestamp = buf.st_mtime;
2255
2256   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2257   g_free (filename);
2258   filename = NULL;
2259
2260   if (load_error != NULL)
2261     goto error;
2262
2263   mime_types = g_key_file_get_keys (key_file, ADDED_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                                                          ADDED_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_added_map,
2279                                 unaliased_type,
2280                                 desktop_file_ids);
2281         }
2282       
2283       g_strfreev (mime_types);
2284     }
2285
2286   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2287                                     NULL, NULL);
2288   if (mime_types != NULL)
2289     {
2290       for (i = 0; mime_types[i] != NULL; i++)
2291         {
2292           desktop_file_ids = g_key_file_get_string_list (key_file,
2293                                                          REMOVED_ASSOCIATIONS_GROUP,
2294                                                          mime_types[i],
2295                                                          NULL,
2296                                                          NULL);
2297           if (desktop_file_ids == NULL)
2298             continue;
2299           
2300           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2301           g_hash_table_replace (dir->mimeapps_list_removed_map,
2302                                 unaliased_type,
2303                                 desktop_file_ids);
2304         }
2305       
2306       g_strfreev (mime_types);
2307     }
2308
2309   g_key_file_free (key_file);
2310   return;
2311   
2312  error:
2313   g_free (filename);
2314   g_key_file_free (key_file);
2315   
2316   if (mime_types != NULL)
2317     g_strfreev (mime_types);
2318   
2319   if (load_error)
2320     g_error_free (load_error);
2321 }
2322
2323 static MimeInfoCacheDir *
2324 mime_info_cache_dir_new (const char *path)
2325 {
2326   MimeInfoCacheDir *dir;
2327
2328   dir = g_new0 (MimeInfoCacheDir, 1);
2329   dir->path = g_strdup (path);
2330   
2331   return dir;
2332 }
2333
2334 static void
2335 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2336 {
2337   if (dir == NULL)
2338     return;
2339   
2340   if (dir->mime_info_cache_map != NULL)
2341     {
2342       destroy_info_cache_map (dir->mime_info_cache_map);
2343       dir->mime_info_cache_map = NULL;
2344       
2345   }
2346   
2347   if (dir->defaults_list_map != NULL)
2348     {
2349       g_hash_table_destroy (dir->defaults_list_map);
2350       dir->defaults_list_map = NULL;
2351     }
2352   
2353   if (dir->mimeapps_list_added_map != NULL)
2354     {
2355       g_hash_table_destroy (dir->mimeapps_list_added_map);
2356       dir->mimeapps_list_added_map = NULL;
2357     }
2358   
2359   if (dir->mimeapps_list_removed_map != NULL)
2360     {
2361       g_hash_table_destroy (dir->mimeapps_list_removed_map);
2362       dir->mimeapps_list_removed_map = NULL;
2363     }
2364   
2365   g_free (dir);
2366 }
2367
2368 static void
2369 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
2370                                          const char        *mime_type,
2371                                          char             **new_desktop_file_ids)
2372 {
2373   GList *desktop_file_ids;
2374   int i;
2375   
2376   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2377                                           mime_type);
2378   
2379   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2380     {
2381       if (!g_list_find_custom (desktop_file_ids, new_desktop_file_ids[i], (GCompareFunc) strcmp))
2382         desktop_file_ids = g_list_append (desktop_file_ids,
2383                                           g_strdup (new_desktop_file_ids[i]));
2384     }
2385   
2386   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2387 }
2388
2389 static void
2390 mime_info_cache_init_dir_lists (void)
2391 {
2392   const char * const *dirs;
2393   int i;
2394   
2395   mime_info_cache = mime_info_cache_new ();
2396   
2397   dirs = get_applications_search_path ();
2398   
2399   for (i = 0; dirs[i] != NULL; i++)
2400     {
2401       MimeInfoCacheDir *dir;
2402       
2403       dir = mime_info_cache_dir_new (dirs[i]);
2404       
2405       if (dir != NULL)
2406         {
2407           mime_info_cache_dir_init (dir);
2408           mime_info_cache_dir_init_defaults_list (dir);
2409           mime_info_cache_dir_init_mimeapps_list (dir);
2410           
2411           mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2412         }
2413     }
2414 }
2415
2416 static void
2417 mime_info_cache_update_dir_lists (void)
2418 {
2419   GList *tmp;
2420   
2421   tmp = mime_info_cache->dirs;
2422   
2423   while (tmp != NULL)
2424     {
2425       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2426
2427       /* No need to do this if we had file monitors... */
2428       mime_info_cache_blow_global_cache ();
2429       mime_info_cache_dir_init (dir);
2430       mime_info_cache_dir_init_defaults_list (dir);
2431       mime_info_cache_dir_init_mimeapps_list (dir);
2432       
2433       tmp = tmp->next;
2434     }
2435 }
2436
2437 static void
2438 mime_info_cache_init (void)
2439 {
2440   G_LOCK (mime_info_cache);
2441   if (mime_info_cache == NULL)
2442     mime_info_cache_init_dir_lists ();
2443   else
2444     {
2445       time_t now;
2446       
2447       time (&now);
2448       if (now >= mime_info_cache->last_stat_time + 10)
2449         {
2450           mime_info_cache_update_dir_lists ();
2451           mime_info_cache->last_stat_time = now;
2452         }
2453     }
2454   
2455   if (mime_info_cache->should_ping_mime_monitor)
2456     {
2457       /* g_idle_add (emit_mime_changed, NULL); */
2458       mime_info_cache->should_ping_mime_monitor = FALSE;
2459     }
2460   
2461   G_UNLOCK (mime_info_cache);
2462 }
2463
2464 static MimeInfoCache *
2465 mime_info_cache_new (void)
2466 {
2467   MimeInfoCache *cache;
2468   
2469   cache = g_new0 (MimeInfoCache, 1);
2470   
2471   cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2472                                                         (GDestroyNotify) g_free,
2473                                                         (GDestroyNotify) g_free);
2474   return cache;
2475 }
2476
2477 static void
2478 mime_info_cache_free (MimeInfoCache *cache)
2479 {
2480   if (cache == NULL)
2481     return;
2482   
2483   g_list_foreach (cache->dirs,
2484                   (GFunc) mime_info_cache_dir_free,
2485                   NULL);
2486   g_list_free (cache->dirs);
2487   g_hash_table_destroy (cache->global_defaults_cache);
2488   g_free (cache);
2489 }
2490
2491 /**
2492  * mime_info_cache_reload:
2493  * @dir: directory path which needs reloading.
2494  * 
2495  * Reload the mime information for the @dir.
2496  */
2497 static void
2498 mime_info_cache_reload (const char *dir)
2499 {
2500   /* FIXME: just reload the dir that needs reloading,
2501    * don't blow the whole cache
2502    */
2503   if (mime_info_cache != NULL)
2504     {
2505       G_LOCK (mime_info_cache);
2506       mime_info_cache_free (mime_info_cache);
2507       mime_info_cache = NULL;
2508       G_UNLOCK (mime_info_cache);
2509     }
2510 }
2511
2512 static GList *
2513 append_desktop_entry (GList      *list, 
2514                       const char *desktop_entry,
2515                       GList      *removed_entries)
2516 {
2517   /* Add if not already in list, and valid */
2518   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2519       !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2520     list = g_list_prepend (list, g_strdup (desktop_entry));
2521   
2522   return list;
2523 }
2524
2525 /**
2526  * get_all_desktop_entries_for_mime_type:
2527  * @mime_type: a mime type.
2528  * @except: NULL or a strv list
2529  *
2530  * Returns all the desktop ids for @mime_type. The desktop files
2531  * are listed in an order so that default applications are listed before
2532  * non-default ones, and handlers for inherited mimetypes are listed
2533  * after the base ones.
2534  *
2535  * Optionally doesn't list the desktop ids given in the @except 
2536  *
2537  * Return value: a #GList containing the desktop ids which claim
2538  *    to handle @mime_type.
2539  */
2540 static GList *
2541 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2542                                        const char **except)
2543 {
2544   GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2545   MimeInfoCacheDir *dir;
2546   char *mime_type;
2547   char **mime_types;
2548   char **default_entries;
2549   char **removed_associations;
2550   int i, j, k;
2551   GPtrArray *array;
2552   char **anc;
2553   
2554   mime_info_cache_init ();
2555
2556   /* collect all ancestors */
2557   mime_types = _g_unix_content_type_get_parents (base_mime_type);
2558   array = g_ptr_array_new ();
2559   for (i = 0; mime_types[i]; i++)
2560     g_ptr_array_add (array, mime_types[i]);
2561   g_free (mime_types);
2562   for (i = 0; i < array->len; i++)
2563     {
2564       anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2565       for (j = 0; anc[j]; j++)
2566         {
2567           for (k = 0; k < array->len; k++)
2568             {
2569               if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2570                 break;
2571             }
2572           if (k == array->len) /* not found */
2573             g_ptr_array_add (array, g_strdup (anc[j]));
2574         }
2575       g_strfreev (anc);
2576     }
2577   g_ptr_array_add (array, NULL);
2578   mime_types = (char **)g_ptr_array_free (array, FALSE);
2579
2580   G_LOCK (mime_info_cache);
2581   
2582   removed_entries = NULL;
2583   desktop_entries = NULL;
2584
2585   for (i = 0; except != NULL && except[i] != NULL; i++)
2586     removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2587   
2588   for (i = 0; mime_types[i] != NULL; i++)
2589     {
2590       mime_type = mime_types[i];
2591
2592       /* Go through all apps listed as defaults */
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           /* First added associations from mimeapps.list */
2600           default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2601           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2602             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2603
2604           /* Then removed associations from mimeapps.list */
2605           removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2606           for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2607             removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2608
2609           /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2610           default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2611           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2612             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2613         }
2614
2615       /* Go through all entries that support the mimetype */
2616       for (dir_list = mime_info_cache->dirs;
2617            dir_list != NULL;
2618            dir_list = dir_list->next) 
2619         {
2620           dir = dir_list->data;
2621         
2622           list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2623           for (tmp = list; tmp != NULL; tmp = tmp->next)
2624             desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2625         }
2626     }
2627   
2628   G_UNLOCK (mime_info_cache);
2629
2630   g_strfreev (mime_types);
2631
2632   g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2633   g_list_free (removed_entries);
2634   
2635   desktop_entries = g_list_reverse (desktop_entries);
2636   
2637   return desktop_entries;
2638 }
2639
2640 /* GDesktopAppInfoLookup interface: */
2641
2642 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface;
2643 G_DEFINE_INTERFACE (GDesktopAppInfoLookup, g_desktop_app_info_lookup, G_TYPE_OBJECT)
2644
2645 static void
2646 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface)
2647 {
2648 }
2649
2650 /**
2651  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2652  * @lookup: a #GDesktopAppInfoLookup
2653  * @uri_scheme: a string containing a URI scheme.
2654  *
2655  * Gets the default application for launching applications 
2656  * using this URI scheme for a particular GDesktopAppInfoLookup
2657  * implementation.
2658  *
2659  * The GDesktopAppInfoLookup interface and this function is used
2660  * to implement g_app_info_get_default_for_uri_scheme() backends
2661  * in a GIO module. There is no reason for applications to use it
2662  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2663  * 
2664  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2665  */
2666 GAppInfo *
2667 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2668                                                       const char            *uri_scheme)
2669 {
2670   GDesktopAppInfoLookupIface *iface;
2671   
2672   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2673
2674   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2675
2676   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2677 }
2678
2679 #define __G_DESKTOP_APP_INFO_C__
2680 #include "gioaliasdef.c"