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