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