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