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