Bug 600620 - Add g_app_info_get_display_name()
[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   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1611
1612   info->filename = NULL;
1613   info->desktop_id = NULL;
1614   
1615   info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1616   info->startup_notify = FALSE;
1617   info->hidden = FALSE;
1618   if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1619     info->exec = g_strconcat (commandline, " %u", NULL);
1620   else
1621     info->exec = g_strconcat (commandline, " %f", NULL);
1622   info->nodisplay = TRUE;
1623   info->binary = binary_from_exec (info->exec);
1624   
1625   if (application_name)
1626     info->name = g_strdup (application_name);
1627   else
1628     {
1629       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1630       split = g_strsplit (commandline, " ", 2);
1631       basename = g_path_get_basename (split[0]);
1632       g_strfreev (split);
1633       info->name = basename;
1634       if (info->name == NULL)
1635         info->name = g_strdup ("custom");
1636     }
1637   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1638   
1639   return G_APP_INFO (info);
1640 }
1641
1642 static void
1643 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1644 {
1645   iface->dup = g_desktop_app_info_dup;
1646   iface->equal = g_desktop_app_info_equal;
1647   iface->get_id = g_desktop_app_info_get_id;
1648   iface->get_name = g_desktop_app_info_get_name;
1649   iface->get_description = g_desktop_app_info_get_description;
1650   iface->get_executable = g_desktop_app_info_get_executable;
1651   iface->get_icon = g_desktop_app_info_get_icon;
1652   iface->launch = g_desktop_app_info_launch;
1653   iface->supports_uris = g_desktop_app_info_supports_uris;
1654   iface->supports_files = g_desktop_app_info_supports_files;
1655   iface->launch_uris = g_desktop_app_info_launch_uris;
1656   iface->should_show = g_desktop_app_info_should_show;
1657   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1658   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1659   iface->add_supports_type = g_desktop_app_info_add_supports_type;
1660   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1661   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1662   iface->can_delete = g_desktop_app_info_can_delete;
1663   iface->do_delete = g_desktop_app_info_delete;
1664   iface->get_commandline = g_desktop_app_info_get_commandline;
1665   iface->get_display_name = g_desktop_app_info_get_display_name;
1666 }
1667
1668 static gboolean
1669 app_info_in_list (GAppInfo *info, 
1670                   GList    *list)
1671 {
1672   while (list != NULL)
1673     {
1674       if (g_app_info_equal (info, list->data))
1675         return TRUE;
1676       list = list->next;
1677     }
1678   return FALSE;
1679 }
1680
1681
1682 /**
1683  * g_app_info_get_all_for_type:
1684  * @content_type: the content type to find a #GAppInfo for
1685  * 
1686  * Gets a list of all #GAppInfo<!-- -->s for a given content type.
1687  *
1688  * Returns: #GList of #GAppInfo<!-- -->s for given @content_type
1689  *    or %NULL on error.
1690  **/
1691 GList *
1692 g_app_info_get_all_for_type (const char *content_type)
1693 {
1694   GList *desktop_entries, *l;
1695   GList *infos;
1696   GDesktopAppInfo *info;
1697
1698   g_return_val_if_fail (content_type != NULL, NULL);
1699   
1700   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1701
1702   infos = NULL;
1703   for (l = desktop_entries; l != NULL; l = l->next)
1704     {
1705       char *desktop_entry = l->data;
1706
1707       info = g_desktop_app_info_new (desktop_entry);
1708       if (info)
1709         {
1710           if (app_info_in_list (G_APP_INFO (info), infos))
1711             g_object_unref (info);
1712           else
1713             infos = g_list_prepend (infos, info);
1714         }
1715       g_free (desktop_entry);
1716     }
1717
1718   g_list_free (desktop_entries);
1719   
1720   return g_list_reverse (infos);
1721 }
1722
1723 /**
1724  * g_app_info_reset_type_associations:
1725  * @content_type: a content type 
1726  *
1727  * Removes all changes to the type associations done by
1728  * g_app_info_set_as_default_for_type(), 
1729  * g_app_info_set_as_default_for_extension(), 
1730  * g_app_info_add_supports_type() or g_app_info_remove_supports_type().
1731  *
1732  * Since: 2.20
1733  */
1734 void
1735 g_app_info_reset_type_associations (const char *content_type)
1736 {
1737   update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1738 }
1739
1740 /**
1741  * g_app_info_get_default_for_type:
1742  * @content_type: the content type to find a #GAppInfo for
1743  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1744  *     support URIs
1745  * 
1746  * Gets the #GAppInfo that corresponds to a given content type.
1747  *
1748  * Returns: #GAppInfo for given @content_type or %NULL on error.
1749  **/
1750 GAppInfo *
1751 g_app_info_get_default_for_type (const char *content_type,
1752                                  gboolean    must_support_uris)
1753 {
1754   GList *desktop_entries, *l;
1755   GAppInfo *info;
1756
1757   g_return_val_if_fail (content_type != NULL, NULL);
1758   
1759   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1760
1761   info = NULL;
1762   for (l = desktop_entries; l != NULL; l = l->next)
1763     {
1764       char *desktop_entry = l->data;
1765
1766       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1767       if (info)
1768         {
1769           if (must_support_uris && !g_app_info_supports_uris (info))
1770             {
1771               g_object_unref (info);
1772               info = NULL;
1773             }
1774           else
1775             break;
1776         }
1777     }
1778   
1779   g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
1780   g_list_free (desktop_entries);
1781   
1782   return info;
1783 }
1784
1785 /**
1786  * g_app_info_get_default_for_uri_scheme:
1787  * @uri_scheme: a string containing a URI scheme.
1788  *
1789  * Gets the default application for launching applications 
1790  * using this URI scheme. A URI scheme is the initial part 
1791  * of the URI, up to but not including the ':', e.g. "http", 
1792  * "ftp" or "sip".
1793  * 
1794  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1795  **/
1796 GAppInfo *
1797 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1798 {
1799   static gsize lookup = 0;
1800   
1801   if (g_once_init_enter (&lookup))
1802     {
1803       gsize setup_value = 1;
1804       GDesktopAppInfoLookup *lookup_instance;
1805       const char *use_this;
1806       GIOExtensionPoint *ep;
1807       GIOExtension *extension;
1808       GList *l;
1809
1810       use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1811       
1812       /* Ensure vfs in modules loaded */
1813       _g_io_modules_ensure_loaded ();
1814       
1815       ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1816
1817       lookup_instance = NULL;
1818       if (use_this)
1819         {
1820           extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1821           if (extension)
1822             lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1823         }
1824       
1825       if (lookup_instance == NULL)
1826         {
1827           for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1828             {
1829               extension = l->data;
1830               lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1831               if (lookup_instance != NULL)
1832                 break;
1833             }
1834         }
1835
1836       if (lookup_instance != NULL)
1837         setup_value = (gsize)lookup_instance;
1838       
1839       g_once_init_leave (&lookup, setup_value);
1840     }
1841
1842   if (lookup == 1)
1843     return NULL;
1844
1845   return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1846                                                                uri_scheme);
1847 }
1848
1849
1850 static void
1851 get_apps_from_dir (GHashTable *apps, 
1852                    const char *dirname, 
1853                    const char *prefix)
1854 {
1855   GDir *dir;
1856   const char *basename;
1857   char *filename, *subprefix, *desktop_id;
1858   gboolean hidden;
1859   GDesktopAppInfo *appinfo;
1860   
1861   dir = g_dir_open (dirname, 0, NULL);
1862   if (dir)
1863     {
1864       while ((basename = g_dir_read_name (dir)) != NULL)
1865         {
1866           filename = g_build_filename (dirname, basename, NULL);
1867           if (g_str_has_suffix (basename, ".desktop"))
1868             {
1869               desktop_id = g_strconcat (prefix, basename, NULL);
1870
1871               /* Use _extended so we catch NULLs too (hidden) */
1872               if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1873                 {
1874                   appinfo = g_desktop_app_info_new_from_filename (filename);
1875                   hidden = FALSE;
1876
1877                   if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1878                     {
1879                       g_object_unref (appinfo);
1880                       appinfo = NULL;
1881                       hidden = TRUE;
1882                     }
1883                                       
1884                   if (appinfo || hidden)
1885                     {
1886                       g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1887
1888                       if (appinfo)
1889                         {
1890                           /* Reuse instead of strdup here */
1891                           appinfo->desktop_id = desktop_id;
1892                           desktop_id = NULL;
1893                         }
1894                     }
1895                 }
1896               g_free (desktop_id);
1897             }
1898           else
1899             {
1900               if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1901                 {
1902                   subprefix = g_strconcat (prefix, basename, "-", NULL);
1903                   get_apps_from_dir (apps, filename, subprefix);
1904                   g_free (subprefix);
1905                 }
1906             }
1907           g_free (filename);
1908         }
1909       g_dir_close (dir);
1910     }
1911 }
1912
1913
1914 /**
1915  * g_app_info_get_all:
1916  *
1917  * Gets a list of all of the applications currently registered 
1918  * on this system.
1919  * 
1920  * For desktop files, this includes applications that have 
1921  * <literal>NoDisplay=true</literal> set or are excluded from 
1922  * display by means of <literal>OnlyShowIn</literal> or
1923  * <literal>NotShowIn</literal>. See g_app_info_should_show().
1924  * The returned list does not include applications which have
1925  * the <literal>Hidden</literal> key set. 
1926  * 
1927  * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
1928  **/
1929 GList *
1930 g_app_info_get_all (void)
1931 {
1932   const char * const *dirs;
1933   GHashTable *apps;
1934   GHashTableIter iter;
1935   gpointer value;
1936   int i;
1937   GList *infos;
1938
1939   dirs = get_applications_search_path ();
1940
1941   apps = g_hash_table_new_full (g_str_hash, g_str_equal,
1942                                 g_free, NULL);
1943
1944   
1945   for (i = 0; dirs[i] != NULL; i++)
1946     get_apps_from_dir (apps, dirs[i], "");
1947
1948
1949   infos = NULL;
1950   g_hash_table_iter_init (&iter, apps);
1951   while (g_hash_table_iter_next (&iter, NULL, &value))
1952     {
1953       if (value)
1954         infos = g_list_prepend (infos, value);
1955     }
1956
1957   g_hash_table_destroy (apps);
1958
1959   return g_list_reverse (infos);
1960 }
1961
1962 /* Cacheing of mimeinfo.cache and defaults.list files */
1963
1964 typedef struct {
1965   char *path;
1966   GHashTable *mime_info_cache_map;
1967   GHashTable *defaults_list_map;
1968   GHashTable *mimeapps_list_added_map;
1969   GHashTable *mimeapps_list_removed_map;
1970   time_t mime_info_cache_timestamp;
1971   time_t defaults_list_timestamp;
1972   time_t mimeapps_list_timestamp;
1973 } MimeInfoCacheDir;
1974
1975 typedef struct {
1976   GList *dirs;                       /* mimeinfo.cache and defaults.list */
1977   GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
1978   time_t last_stat_time;
1979   guint should_ping_mime_monitor : 1;
1980 } MimeInfoCache;
1981
1982 static MimeInfoCache *mime_info_cache = NULL;
1983 G_LOCK_DEFINE_STATIC (mime_info_cache);
1984
1985 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
1986                                                      const char        *mime_type,
1987                                                      char             **new_desktop_file_ids);
1988
1989 static MimeInfoCache * mime_info_cache_new (void);
1990
1991 static void
1992 destroy_info_cache_value (gpointer  key, 
1993                           GList    *value, 
1994                           gpointer  data)
1995 {
1996   g_list_foreach (value, (GFunc)g_free, NULL);
1997   g_list_free (value);
1998 }
1999
2000 static void
2001 destroy_info_cache_map (GHashTable *info_cache_map)
2002 {
2003   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2004   g_hash_table_destroy (info_cache_map);
2005 }
2006
2007 static gboolean
2008 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2009                                  const char       *cache_file,
2010                                  time_t           *timestamp)
2011 {
2012   struct stat buf;
2013   char *filename;
2014   
2015   filename = g_build_filename (dir->path, cache_file, NULL);
2016   
2017   if (g_stat (filename, &buf) < 0)
2018     {
2019       g_free (filename);
2020       return TRUE;
2021     }
2022   g_free (filename);
2023
2024   if (buf.st_mtime != *timestamp) 
2025     return TRUE;
2026   
2027   return FALSE;
2028 }
2029
2030 /* Call with lock held */
2031 static gboolean
2032 remove_all (gpointer  key,
2033             gpointer  value,
2034             gpointer  user_data)
2035 {
2036   return TRUE;
2037 }
2038
2039
2040 static void
2041 mime_info_cache_blow_global_cache (void)
2042 {
2043   g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2044                                remove_all, NULL);
2045 }
2046
2047 static void
2048 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2049 {
2050   GError *load_error;
2051   GKeyFile *key_file;
2052   gchar *filename, **mime_types;
2053   int i;
2054   struct stat buf;
2055   
2056   load_error = NULL;
2057   mime_types = NULL;
2058   
2059   if (dir->mime_info_cache_map != NULL &&
2060       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2061                                         &dir->mime_info_cache_timestamp))
2062     return;
2063   
2064   if (dir->mime_info_cache_map != NULL)
2065     destroy_info_cache_map (dir->mime_info_cache_map);
2066   
2067   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2068                                                     (GDestroyNotify) g_free,
2069                                                     NULL);
2070   
2071   key_file = g_key_file_new ();
2072   
2073   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2074   
2075   if (g_stat (filename, &buf) < 0)
2076     goto error;
2077   
2078   if (dir->mime_info_cache_timestamp > 0) 
2079     mime_info_cache->should_ping_mime_monitor = TRUE;
2080   
2081   dir->mime_info_cache_timestamp = buf.st_mtime;
2082   
2083   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2084   
2085   g_free (filename);
2086   filename = NULL;
2087   
2088   if (load_error != NULL)
2089     goto error;
2090   
2091   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2092                                     NULL, &load_error);
2093   
2094   if (load_error != NULL)
2095     goto error;
2096   
2097   for (i = 0; mime_types[i] != NULL; i++)
2098     {
2099       gchar **desktop_file_ids;
2100       char *unaliased_type;
2101       desktop_file_ids = g_key_file_get_string_list (key_file,
2102                                                      MIME_CACHE_GROUP,
2103                                                      mime_types[i],
2104                                                      NULL,
2105                                                      NULL);
2106       
2107       if (desktop_file_ids == NULL)
2108         continue;
2109
2110       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2111       mime_info_cache_dir_add_desktop_entries (dir,
2112                                                unaliased_type,
2113                                                desktop_file_ids);
2114       g_free (unaliased_type);
2115     
2116       g_strfreev (desktop_file_ids);
2117     }
2118   
2119   g_strfreev (mime_types);
2120   g_key_file_free (key_file);
2121   
2122   return;
2123  error:
2124   g_free (filename);
2125   g_key_file_free (key_file);
2126   
2127   if (mime_types != NULL)
2128     g_strfreev (mime_types);
2129   
2130   if (load_error)
2131     g_error_free (load_error);
2132 }
2133
2134 static void
2135 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2136 {
2137   GKeyFile *key_file;
2138   GError *load_error;
2139   gchar *filename, **mime_types;
2140   char *unaliased_type;
2141   char **desktop_file_ids;
2142   int i;
2143   struct stat buf;
2144
2145   load_error = NULL;
2146   mime_types = NULL;
2147
2148   if (dir->defaults_list_map != NULL &&
2149       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2150                                         &dir->defaults_list_timestamp))
2151     return;
2152   
2153   if (dir->defaults_list_map != NULL)
2154     g_hash_table_destroy (dir->defaults_list_map);
2155   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2156                                                   g_free, (GDestroyNotify)g_strfreev);
2157   
2158
2159   key_file = g_key_file_new ();
2160   
2161   filename = g_build_filename (dir->path, "defaults.list", NULL);
2162   if (g_stat (filename, &buf) < 0)
2163     goto error;
2164
2165   if (dir->defaults_list_timestamp > 0) 
2166     mime_info_cache->should_ping_mime_monitor = TRUE;
2167
2168   dir->defaults_list_timestamp = buf.st_mtime;
2169
2170   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2171   g_free (filename);
2172   filename = NULL;
2173
2174   if (load_error != NULL)
2175     goto error;
2176
2177   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2178                                     NULL, NULL);
2179   if (mime_types != NULL)
2180     {
2181       for (i = 0; mime_types[i] != NULL; i++)
2182         {
2183           desktop_file_ids = g_key_file_get_string_list (key_file,
2184                                                          DEFAULT_APPLICATIONS_GROUP,
2185                                                          mime_types[i],
2186                                                          NULL,
2187                                                          NULL);
2188           if (desktop_file_ids == NULL)
2189             continue;
2190           
2191           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2192           g_hash_table_replace (dir->defaults_list_map,
2193                                 unaliased_type,
2194                                 desktop_file_ids);
2195         }
2196       
2197       g_strfreev (mime_types);
2198     }
2199
2200   g_key_file_free (key_file);
2201   return;
2202   
2203  error:
2204   g_free (filename);
2205   g_key_file_free (key_file);
2206   
2207   if (mime_types != NULL)
2208     g_strfreev (mime_types);
2209   
2210   if (load_error)
2211     g_error_free (load_error);
2212 }
2213
2214 static void
2215 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2216 {
2217   GKeyFile *key_file;
2218   GError *load_error;
2219   gchar *filename, **mime_types;
2220   char *unaliased_type;
2221   char **desktop_file_ids;
2222   int i;
2223   struct stat buf;
2224
2225   load_error = NULL;
2226   mime_types = NULL;
2227
2228   if (dir->mimeapps_list_added_map != NULL &&
2229       !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2230                                         &dir->mimeapps_list_timestamp))
2231     return;
2232   
2233   if (dir->mimeapps_list_added_map != NULL)
2234     g_hash_table_destroy (dir->mimeapps_list_added_map);
2235   dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2236                                                         g_free, (GDestroyNotify)g_strfreev);
2237   
2238   if (dir->mimeapps_list_removed_map != NULL)
2239     g_hash_table_destroy (dir->mimeapps_list_removed_map);
2240   dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2241                                                           g_free, (GDestroyNotify)g_strfreev);
2242
2243   key_file = g_key_file_new ();
2244   
2245   filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2246   if (g_stat (filename, &buf) < 0)
2247     goto error;
2248
2249   if (dir->mimeapps_list_timestamp > 0) 
2250     mime_info_cache->should_ping_mime_monitor = TRUE;
2251
2252   dir->mimeapps_list_timestamp = buf.st_mtime;
2253
2254   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2255   g_free (filename);
2256   filename = NULL;
2257
2258   if (load_error != NULL)
2259     goto error;
2260
2261   mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2262                                     NULL, NULL);
2263   if (mime_types != NULL)
2264     {
2265       for (i = 0; mime_types[i] != NULL; i++)
2266         {
2267           desktop_file_ids = g_key_file_get_string_list (key_file,
2268                                                          ADDED_ASSOCIATIONS_GROUP,
2269                                                          mime_types[i],
2270                                                          NULL,
2271                                                          NULL);
2272           if (desktop_file_ids == NULL)
2273             continue;
2274           
2275           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2276           g_hash_table_replace (dir->mimeapps_list_added_map,
2277                                 unaliased_type,
2278                                 desktop_file_ids);
2279         }
2280       
2281       g_strfreev (mime_types);
2282     }
2283
2284   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2285                                     NULL, NULL);
2286   if (mime_types != NULL)
2287     {
2288       for (i = 0; mime_types[i] != NULL; i++)
2289         {
2290           desktop_file_ids = g_key_file_get_string_list (key_file,
2291                                                          REMOVED_ASSOCIATIONS_GROUP,
2292                                                          mime_types[i],
2293                                                          NULL,
2294                                                          NULL);
2295           if (desktop_file_ids == NULL)
2296             continue;
2297           
2298           unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2299           g_hash_table_replace (dir->mimeapps_list_removed_map,
2300                                 unaliased_type,
2301                                 desktop_file_ids);
2302         }
2303       
2304       g_strfreev (mime_types);
2305     }
2306
2307   g_key_file_free (key_file);
2308   return;
2309   
2310  error:
2311   g_free (filename);
2312   g_key_file_free (key_file);
2313   
2314   if (mime_types != NULL)
2315     g_strfreev (mime_types);
2316   
2317   if (load_error)
2318     g_error_free (load_error);
2319 }
2320
2321 static MimeInfoCacheDir *
2322 mime_info_cache_dir_new (const char *path)
2323 {
2324   MimeInfoCacheDir *dir;
2325
2326   dir = g_new0 (MimeInfoCacheDir, 1);
2327   dir->path = g_strdup (path);
2328   
2329   return dir;
2330 }
2331
2332 static void
2333 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2334 {
2335   if (dir == NULL)
2336     return;
2337   
2338   if (dir->mime_info_cache_map != NULL)
2339     {
2340       destroy_info_cache_map (dir->mime_info_cache_map);
2341       dir->mime_info_cache_map = NULL;
2342       
2343   }
2344   
2345   if (dir->defaults_list_map != NULL)
2346     {
2347       g_hash_table_destroy (dir->defaults_list_map);
2348       dir->defaults_list_map = NULL;
2349     }
2350   
2351   if (dir->mimeapps_list_added_map != NULL)
2352     {
2353       g_hash_table_destroy (dir->mimeapps_list_added_map);
2354       dir->mimeapps_list_added_map = NULL;
2355     }
2356   
2357   if (dir->mimeapps_list_removed_map != NULL)
2358     {
2359       g_hash_table_destroy (dir->mimeapps_list_removed_map);
2360       dir->mimeapps_list_removed_map = NULL;
2361     }
2362   
2363   g_free (dir);
2364 }
2365
2366 static void
2367 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
2368                                          const char        *mime_type,
2369                                          char             **new_desktop_file_ids)
2370 {
2371   GList *desktop_file_ids;
2372   int i;
2373   
2374   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2375                                           mime_type);
2376   
2377   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2378     {
2379       if (!g_list_find_custom (desktop_file_ids, new_desktop_file_ids[i], (GCompareFunc) strcmp))
2380         desktop_file_ids = g_list_append (desktop_file_ids,
2381                                           g_strdup (new_desktop_file_ids[i]));
2382     }
2383   
2384   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2385 }
2386
2387 static void
2388 mime_info_cache_init_dir_lists (void)
2389 {
2390   const char * const *dirs;
2391   int i;
2392   
2393   mime_info_cache = mime_info_cache_new ();
2394   
2395   dirs = get_applications_search_path ();
2396   
2397   for (i = 0; dirs[i] != NULL; i++)
2398     {
2399       MimeInfoCacheDir *dir;
2400       
2401       dir = mime_info_cache_dir_new (dirs[i]);
2402       
2403       if (dir != NULL)
2404         {
2405           mime_info_cache_dir_init (dir);
2406           mime_info_cache_dir_init_defaults_list (dir);
2407           mime_info_cache_dir_init_mimeapps_list (dir);
2408           
2409           mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2410         }
2411     }
2412 }
2413
2414 static void
2415 mime_info_cache_update_dir_lists (void)
2416 {
2417   GList *tmp;
2418   
2419   tmp = mime_info_cache->dirs;
2420   
2421   while (tmp != NULL)
2422     {
2423       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2424
2425       /* No need to do this if we had file monitors... */
2426       mime_info_cache_blow_global_cache ();
2427       mime_info_cache_dir_init (dir);
2428       mime_info_cache_dir_init_defaults_list (dir);
2429       mime_info_cache_dir_init_mimeapps_list (dir);
2430       
2431       tmp = tmp->next;
2432     }
2433 }
2434
2435 static void
2436 mime_info_cache_init (void)
2437 {
2438   G_LOCK (mime_info_cache);
2439   if (mime_info_cache == NULL)
2440     mime_info_cache_init_dir_lists ();
2441   else
2442     {
2443       time_t now;
2444       
2445       time (&now);
2446       if (now >= mime_info_cache->last_stat_time + 10)
2447         {
2448           mime_info_cache_update_dir_lists ();
2449           mime_info_cache->last_stat_time = now;
2450         }
2451     }
2452   
2453   if (mime_info_cache->should_ping_mime_monitor)
2454     {
2455       /* g_idle_add (emit_mime_changed, NULL); */
2456       mime_info_cache->should_ping_mime_monitor = FALSE;
2457     }
2458   
2459   G_UNLOCK (mime_info_cache);
2460 }
2461
2462 static MimeInfoCache *
2463 mime_info_cache_new (void)
2464 {
2465   MimeInfoCache *cache;
2466   
2467   cache = g_new0 (MimeInfoCache, 1);
2468   
2469   cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2470                                                         (GDestroyNotify) g_free,
2471                                                         (GDestroyNotify) g_free);
2472   return cache;
2473 }
2474
2475 static void
2476 mime_info_cache_free (MimeInfoCache *cache)
2477 {
2478   if (cache == NULL)
2479     return;
2480   
2481   g_list_foreach (cache->dirs,
2482                   (GFunc) mime_info_cache_dir_free,
2483                   NULL);
2484   g_list_free (cache->dirs);
2485   g_hash_table_destroy (cache->global_defaults_cache);
2486   g_free (cache);
2487 }
2488
2489 /**
2490  * mime_info_cache_reload:
2491  * @dir: directory path which needs reloading.
2492  * 
2493  * Reload the mime information for the @dir.
2494  */
2495 static void
2496 mime_info_cache_reload (const char *dir)
2497 {
2498   /* FIXME: just reload the dir that needs reloading,
2499    * don't blow the whole cache
2500    */
2501   if (mime_info_cache != NULL)
2502     {
2503       G_LOCK (mime_info_cache);
2504       mime_info_cache_free (mime_info_cache);
2505       mime_info_cache = NULL;
2506       G_UNLOCK (mime_info_cache);
2507     }
2508 }
2509
2510 static GList *
2511 append_desktop_entry (GList      *list, 
2512                       const char *desktop_entry,
2513                       GList      *removed_entries)
2514 {
2515   /* Add if not already in list, and valid */
2516   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2517       !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2518     list = g_list_prepend (list, g_strdup (desktop_entry));
2519   
2520   return list;
2521 }
2522
2523 /**
2524  * get_all_desktop_entries_for_mime_type:
2525  * @mime_type: a mime type.
2526  * @except: NULL or a strv list
2527  *
2528  * Returns all the desktop ids for @mime_type. The desktop files
2529  * are listed in an order so that default applications are listed before
2530  * non-default ones, and handlers for inherited mimetypes are listed
2531  * after the base ones.
2532  *
2533  * Optionally doesn't list the desktop ids given in the @except 
2534  *
2535  * Return value: a #GList containing the desktop ids which claim
2536  *    to handle @mime_type.
2537  */
2538 static GList *
2539 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2540                                        const char **except)
2541 {
2542   GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2543   MimeInfoCacheDir *dir;
2544   char *mime_type;
2545   char **mime_types;
2546   char **default_entries;
2547   char **removed_associations;
2548   int i, j, k;
2549   GPtrArray *array;
2550   char **anc;
2551   
2552   mime_info_cache_init ();
2553
2554   /* collect all ancestors */
2555   mime_types = _g_unix_content_type_get_parents (base_mime_type);
2556   array = g_ptr_array_new ();
2557   for (i = 0; mime_types[i]; i++)
2558     g_ptr_array_add (array, mime_types[i]);
2559   g_free (mime_types);
2560   for (i = 0; i < array->len; i++)
2561     {
2562       anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2563       for (j = 0; anc[j]; j++)
2564         {
2565           for (k = 0; k < array->len; k++)
2566             {
2567               if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2568                 break;
2569             }
2570           if (k == array->len) /* not found */
2571             g_ptr_array_add (array, g_strdup (anc[j]));
2572         }
2573       g_strfreev (anc);
2574     }
2575   g_ptr_array_add (array, NULL);
2576   mime_types = (char **)g_ptr_array_free (array, FALSE);
2577
2578   G_LOCK (mime_info_cache);
2579   
2580   removed_entries = NULL;
2581   desktop_entries = NULL;
2582
2583   for (i = 0; except != NULL && except[i] != NULL; i++)
2584     removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2585   
2586   for (i = 0; mime_types[i] != NULL; i++)
2587     {
2588       mime_type = mime_types[i];
2589
2590       /* Go through all apps listed as defaults */
2591       for (dir_list = mime_info_cache->dirs;
2592            dir_list != NULL;
2593            dir_list = dir_list->next)
2594         {
2595           dir = dir_list->data;
2596
2597           /* First added associations from mimeapps.list */
2598           default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2599           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2600             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2601
2602           /* Then removed associations from mimeapps.list */
2603           removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2604           for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2605             removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2606
2607           /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2608           default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2609           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2610             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2611         }
2612
2613       /* Go through all entries that support the mimetype */
2614       for (dir_list = mime_info_cache->dirs;
2615            dir_list != NULL;
2616            dir_list = dir_list->next) 
2617         {
2618           dir = dir_list->data;
2619         
2620           list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2621           for (tmp = list; tmp != NULL; tmp = tmp->next)
2622             desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2623         }
2624     }
2625   
2626   G_UNLOCK (mime_info_cache);
2627
2628   g_strfreev (mime_types);
2629
2630   g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2631   g_list_free (removed_entries);
2632   
2633   desktop_entries = g_list_reverse (desktop_entries);
2634   
2635   return desktop_entries;
2636 }
2637
2638 /* GDesktopAppInfoLookup interface: */
2639
2640 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2641 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2642                                                   gpointer class_data);
2643
2644 GType
2645 g_desktop_app_info_lookup_get_type (void)
2646 {
2647   static volatile gsize g_define_type_id__volatile = 0;
2648
2649   if (g_once_init_enter (&g_define_type_id__volatile))
2650     {
2651       const GTypeInfo desktop_app_info_lookup_info =
2652       {
2653         sizeof (GDesktopAppInfoLookupIface), /* class_size */
2654         g_desktop_app_info_lookup_base_init,   /* base_init */
2655         NULL,           /* base_finalize */
2656         g_desktop_app_info_lookup_class_init,
2657         NULL,           /* class_finalize */
2658         NULL,           /* class_data */
2659         0,
2660         0,              /* n_preallocs */
2661         NULL
2662       };
2663       GType g_define_type_id =
2664         g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2665                                 &desktop_app_info_lookup_info, 0);
2666
2667       g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2668
2669       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2670     }
2671
2672   return g_define_type_id__volatile;
2673 }
2674
2675 static void
2676 g_desktop_app_info_lookup_class_init (gpointer g_class,
2677                                       gpointer class_data)
2678 {
2679 }
2680
2681 static void
2682 g_desktop_app_info_lookup_base_init (gpointer g_class)
2683 {
2684 }
2685
2686 /**
2687  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2688  * @lookup: a #GDesktopAppInfoLookup
2689  * @uri_scheme: a string containing a URI scheme.
2690  *
2691  * Gets the default application for launching applications 
2692  * using this URI scheme for a particular GDesktopAppInfoLookup
2693  * implementation.
2694  *
2695  * The GDesktopAppInfoLookup interface and this function is used
2696  * to implement g_app_info_get_default_for_uri_scheme() backends
2697  * in a GIO module. There is no reason for applications to use it
2698  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2699  * 
2700  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2701  */
2702 GAppInfo *
2703 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2704                                                       const char            *uri_scheme)
2705 {
2706   GDesktopAppInfoLookupIface *iface;
2707   
2708   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2709
2710   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2711
2712   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2713 }
2714
2715 #define __G_DESKTOP_APP_INFO_C__
2716 #include "gioaliasdef.c"