Don't leak GKeyFile
[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 "gioerror.h"
38 #include "gthemedicon.h"
39 #include "gfileicon.h"
40 #include <glib/gstdio.h>
41 #include "glibintl.h"
42
43 #include "gioalias.h"
44
45 /**
46  * SECTION:gdesktopappinfo
47  * @short_description: Application information from desktop files
48  * @include: gio/gdesktopappinfo.h 
49  * 
50  * #GDesktopAppInfo is an implementation of #GAppInfo based on
51  * desktop files.
52  *
53  **/
54
55 #define DEFAULT_APPLICATIONS_GROUP  "Default Applications" 
56 #define MIME_CACHE_GROUP            "MIME Cache"
57
58 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
59
60 static GList *get_all_desktop_entries_for_mime_type (const char *base_mime_type);
61 static void mime_info_cache_reload (const char *dir);
62
63 /**
64  * GDesktopAppInfo:
65  * 
66  * Information about an installed application from a desktop file.
67  */
68 struct _GDesktopAppInfo
69 {
70   GObject parent_instance;
71
72   char *desktop_id;
73   char *filename;
74
75   char *name;
76   /* FIXME: what about GenericName ? */
77   char *comment;
78   char *icon_name;
79   GIcon *icon;
80   char **only_show_in;
81   char **not_show_in;
82   char *try_exec;
83   char *exec;
84   char *binary;
85   char *path;
86
87   guint nodisplay       : 1;
88   guint hidden          : 1;
89   guint terminal        : 1;
90   guint startup_notify  : 1;
91   /* FIXME: what about StartupWMClass ? */
92 };
93
94 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
95                          G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
96                                                 g_desktop_app_info_iface_init))
97
98 static gpointer
99 search_path_init (gpointer data)
100 {
101   char **args = NULL;
102   const char * const *data_dirs;
103   const char *user_data_dir;
104   int i, length, j;
105
106   data_dirs = g_get_system_data_dirs ();
107   length = g_strv_length ((char **) data_dirs);
108   
109   args = g_new (char *, length + 2);
110   
111   j = 0;
112   user_data_dir = g_get_user_data_dir ();
113   args[j++] = g_build_filename (user_data_dir, "applications", NULL);
114   for (i = 0; i < length; i++)
115     args[j++] = g_build_filename (data_dirs[i],
116                                   "applications", NULL);
117   args[j++] = NULL;
118   
119   return args;
120 }
121   
122 static const char * const *
123 get_applications_search_path (void)
124 {
125   static GOnce once_init = G_ONCE_INIT;
126   return g_once (&once_init, search_path_init, NULL);
127 }
128
129 static void
130 g_desktop_app_info_finalize (GObject *object)
131 {
132   GDesktopAppInfo *info;
133
134   info = G_DESKTOP_APP_INFO (object);
135
136   g_free (info->desktop_id);
137   g_free (info->filename);
138   g_free (info->name);
139   g_free (info->comment);
140   g_free (info->icon_name);
141   if (info->icon)
142     g_object_unref (info->icon);
143   g_strfreev (info->only_show_in);
144   g_strfreev (info->not_show_in);
145   g_free (info->try_exec);
146   g_free (info->exec);
147   g_free (info->binary);
148   g_free (info->path);
149   
150   G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
151 }
152
153 static void
154 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
155 {
156   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
157   
158   gobject_class->finalize = g_desktop_app_info_finalize;
159 }
160
161 static void
162 g_desktop_app_info_init (GDesktopAppInfo *local)
163 {
164 }
165
166 /**
167  * g_desktop_app_info_new_from_filename:
168  * @filename: a string containing a file name.
169  * 
170  * Creates a new #GDesktopAppInfo.
171  *
172  * Returns: a new #GDesktopAppInfo or %NULL on error.
173  **/
174 GDesktopAppInfo *
175 g_desktop_app_info_new_from_filename (const char *filename)
176 {
177   GDesktopAppInfo *info;
178   GKeyFile *key_file;
179   char *start_group;
180   char *type;
181   char *try_exec;
182   
183   key_file = g_key_file_new ();
184   
185   if (!g_key_file_load_from_file (key_file,
186                                   filename,
187                                   G_KEY_FILE_NONE,
188                                   NULL))
189     {
190       g_key_file_free (key_file);
191       return NULL;
192     }
193
194   start_group = g_key_file_get_start_group (key_file);
195   if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
196     {
197       g_free (start_group);
198       g_key_file_free (key_file);
199       return NULL;
200     }
201   g_free (start_group);
202
203   type = g_key_file_get_string (key_file,
204                                 G_KEY_FILE_DESKTOP_GROUP,
205                                 G_KEY_FILE_DESKTOP_KEY_TYPE,
206                                 NULL);
207   if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
208     {
209       g_free (type);
210       g_key_file_free (key_file);
211       return NULL;
212     }
213   g_free (type);
214
215   try_exec = g_key_file_get_string (key_file,
216                                     G_KEY_FILE_DESKTOP_GROUP,
217                                     G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
218                                     NULL);
219   if (try_exec)
220     {
221       char *t;
222       t = g_find_program_in_path (try_exec);
223       if (t == NULL)
224         {
225           g_free (try_exec);
226           g_key_file_free (key_file);
227           return NULL;
228         }
229       g_free (t);
230     }
231
232   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
233   info->filename = g_strdup (filename);
234
235   info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
236   info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
237   info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
238   info->icon_name =  g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
239   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);
240   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);
241   info->try_exec = try_exec;
242   info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
243   info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
244   info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
245   info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
246   info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
247
248   g_key_file_free (key_file);
249   
250   info->icon = NULL;
251   if (info->icon_name)
252     {
253       if (g_path_is_absolute (info->icon_name))
254         {
255           GFile *file;
256           
257           file = g_file_new_for_path (info->icon_name);
258           info->icon = g_file_icon_new (file);
259           g_object_unref (file);
260         }
261       else
262         info->icon = g_themed_icon_new (info->icon_name);
263     }
264   
265   if (info->exec)
266     {
267       char *p, *start;
268
269       p = info->exec;
270       while (*p == ' ')
271         p++;
272       start = p;
273       while (*p != ' ' && *p != 0)
274         p++;
275       
276       info->binary = g_strndup (start, p - start);
277     }
278   
279   return info;
280 }
281
282 /**
283  * g_desktop_app_info_new:
284  * @desktop_id: the desktop file id
285  * 
286  * Creates a new #GDesktopAppInfo.
287  * 
288  * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
289  **/
290 GDesktopAppInfo *
291 g_desktop_app_info_new (const char *desktop_id)
292 {
293   GDesktopAppInfo *appinfo;
294   const char * const *dirs;
295   int i;
296
297   dirs = get_applications_search_path ();
298
299   for (i = 0; dirs[i] != NULL; i++)
300     {
301       char *basename;
302       char *filename;
303       char *p;
304
305       filename = g_build_filename (dirs[i], desktop_id, NULL);
306       appinfo = g_desktop_app_info_new_from_filename (filename);
307       g_free (filename);
308       if (appinfo != NULL)
309         {
310           goto found;
311         }
312
313       basename = g_strdup (desktop_id);
314       p = basename;
315       while ((p = strchr (p, '-')) != NULL)
316         {
317           *p = '/';
318           
319           filename = g_build_filename (dirs[i], basename, NULL);
320           appinfo = g_desktop_app_info_new_from_filename (filename);
321           g_free (filename);
322           if (appinfo != NULL)
323             {
324               g_free (basename);
325               goto found;
326             }
327           *p = '-';
328           p++;
329         }
330     }
331   
332   return NULL;
333
334  found:
335   appinfo->desktop_id = g_strdup (desktop_id);
336
337   if (g_desktop_app_info_get_is_hidden (appinfo))
338     {
339       g_object_unref (appinfo);
340       appinfo = NULL;
341     }
342   
343   return appinfo;
344 }
345
346 static GAppInfo *
347 g_desktop_app_info_dup (GAppInfo *appinfo)
348 {
349   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
350   GDesktopAppInfo *new_info;
351   
352   new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
353
354   new_info->filename = g_strdup (info->filename);
355   new_info->desktop_id = g_strdup (info->desktop_id);
356   
357   new_info->name = g_strdup (info->name);
358   new_info->comment = g_strdup (info->comment);
359   new_info->nodisplay = info->nodisplay;
360   new_info->icon_name = g_strdup (info->icon_name);
361   new_info->icon = g_object_ref (info->icon);
362   new_info->only_show_in = g_strdupv (info->only_show_in);
363   new_info->not_show_in = g_strdupv (info->not_show_in);
364   new_info->try_exec = g_strdup (info->try_exec);
365   new_info->exec = g_strdup (info->exec);
366   new_info->binary = g_strdup (info->binary);
367   new_info->path = g_strdup (info->path);
368   new_info->hidden = info->hidden;
369   new_info->terminal = info->terminal;
370   new_info->startup_notify = info->startup_notify;
371   
372   return G_APP_INFO (new_info);
373 }
374
375 static gboolean
376 g_desktop_app_info_equal (GAppInfo *appinfo1,
377                           GAppInfo *appinfo2)
378 {
379   GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
380   GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
381
382   if (info1->desktop_id == NULL ||
383       info2->desktop_id == NULL)
384     return FALSE;
385
386   return strcmp (info1->desktop_id, info2->desktop_id) == 0;
387 }
388
389 static const char *
390 g_desktop_app_info_get_id (GAppInfo *appinfo)
391 {
392   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
393
394   return info->desktop_id;
395 }
396
397 static const char *
398 g_desktop_app_info_get_name (GAppInfo *appinfo)
399 {
400   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
401
402   if (info->name == NULL)
403     return _("Unnamed");
404   return info->name;
405 }
406
407 /**
408  * g_desktop_app_info_get_is_hidden:
409  * @info: a #GDesktopAppInfo.
410  *
411  * A desktop file is hidden if the Hidden key in it is
412  * set to True.
413  *
414  * Returns: %TRUE if hidden, %FALSE otherwise. 
415  **/
416 gboolean
417 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
418 {
419   return info->hidden;
420 }
421
422 static const char *
423 g_desktop_app_info_get_description (GAppInfo *appinfo)
424 {
425   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
426   
427   return info->comment;
428 }
429
430 static const char *
431 g_desktop_app_info_get_executable (GAppInfo *appinfo)
432 {
433   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
434   
435   return info->binary;
436 }
437
438 static GIcon *
439 g_desktop_app_info_get_icon (GAppInfo *appinfo)
440 {
441   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
442
443   return info->icon;
444 }
445
446 static char *
447 expand_macro_single (char macro, GFile *file)
448 {
449   char *result = NULL;
450   char *uri, *path;
451
452   path = g_file_get_path (file);
453   uri = g_file_get_uri (file);
454   
455   switch (macro)
456     {
457     case 'u':
458     case 'U':   
459       result = g_shell_quote (uri);
460       break;
461     case 'f':
462     case 'F':
463       if (path)
464         result = g_shell_quote (path);
465       break;
466     case 'd':
467     case 'D':
468       if (path)
469         result = g_shell_quote (g_path_get_dirname (path));
470       break;
471     case 'n':
472     case 'N':
473       if (path)
474         result = g_shell_quote (g_path_get_basename (path));
475       break;
476     }
477
478   g_free (path);
479   g_free (uri);
480   
481   return result;
482 }
483
484 static void
485 expand_macro (char              macro, 
486               GString          *exec, 
487               GDesktopAppInfo  *info, 
488               GList           **file_list)
489 {
490   GList *files = *file_list;
491   char *expanded;
492   
493   g_return_if_fail (exec != NULL);
494   
495   switch (macro)
496     {
497     case 'u':
498     case 'f':
499     case 'd':
500     case 'n':
501       if (files)
502         {
503           expanded = expand_macro_single (macro, files->data);
504           if (expanded)
505             {
506               g_string_append (exec, expanded);
507               g_free (expanded);
508             }
509           files = files->next;
510         }
511
512       break;
513
514     case 'U':   
515     case 'F':
516     case 'D':
517     case 'N':
518       while (files)
519         {
520           expanded = expand_macro_single (macro, files->data);
521           if (expanded)
522             {
523               g_string_append (exec, expanded);
524               g_free (expanded);
525             }
526           
527           files = files->next;
528           
529           if (files != NULL && expanded)
530             g_string_append_c (exec, ' ');
531         }
532
533       break;
534
535     case 'i':
536       if (info->icon_name)
537         {
538           g_string_append (exec, "--icon ");
539           g_string_append (exec, info->icon_name);
540         }
541       break;
542
543     case 'c':
544       if (info->name) 
545         g_string_append (exec, info->name);
546       break;
547
548     case 'k':
549       if (info->filename) 
550         g_string_append (exec, info->filename);
551       break;
552
553     case 'm': /* deprecated */
554       break;
555
556     case '%':
557       g_string_append_c (exec, '%');
558       break;
559     }
560   
561   *file_list = files;
562 }
563
564 static gboolean
565 expand_application_parameters (GDesktopAppInfo   *info,
566                                GList            **files,
567                                int               *argc,
568                                char            ***argv,
569                                GError           **error)
570 {
571   GList *file_list = *files;
572   const char *p = info->exec;
573   GString *expanded_exec = g_string_new (NULL);
574   gboolean res;
575   
576   if (info->exec == NULL)
577     {
578       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
579                    _("Desktop file didn't specify Exec field"));
580       return FALSE;
581     }
582   
583   while (*p)
584     {
585       if (p[0] == '%' && p[1] != '\0')
586         {
587           expand_macro (p[1], expanded_exec, info, files);
588           p++;
589         }
590       else
591         g_string_append_c (expanded_exec, *p);
592       
593       p++;
594     }
595   
596   /* No file substitutions */
597   if (file_list == *files && file_list != NULL)
598     {
599       /* If there is no macro default to %f. This is also what KDE does */
600       g_string_append_c (expanded_exec, ' ');
601       expand_macro ('f', expanded_exec, info, files);
602     }
603   
604   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
605   g_string_free (expanded_exec, TRUE);
606   return res;
607 }
608
609 static gboolean
610 prepend_terminal_to_vector (int    *argc,
611                             char ***argv)
612 {
613 #ifndef G_OS_WIN32
614   char **real_argv;
615   int real_argc;
616   int i, j;
617   char **term_argv = NULL;
618   int term_argc = 0;
619   char *check;
620   char **the_argv;
621   
622   g_return_val_if_fail (argc != NULL, FALSE);
623   g_return_val_if_fail (argv != NULL, FALSE);
624         
625   /* sanity */
626   if(*argv == NULL)
627     *argc = 0;
628   
629   the_argv = *argv;
630
631   /* compute size if not given */
632   if (*argc < 0)
633     {
634       for (i = 0; the_argv[i] != NULL; i++)
635         ;
636       *argc = i;
637     }
638   
639   term_argc = 2;
640   term_argv = g_new0 (char *, 3);
641
642   check = g_find_program_in_path ("gnome-terminal");
643   if (check != NULL)
644     {
645       term_argv[0] = check;
646       /* Note that gnome-terminal takes -x and
647        * as -e in gnome-terminal is broken we use that. */
648       term_argv[1] = g_strdup ("-x");
649     }
650   else
651     {
652       if (check == NULL)
653         check = g_find_program_in_path ("nxterm");
654       if (check == NULL)
655         check = g_find_program_in_path ("color-xterm");
656       if (check == NULL)
657         check = g_find_program_in_path ("rxvt");
658       if (check == NULL)
659         check = g_find_program_in_path ("xterm");
660       if (check == NULL)
661         check = g_find_program_in_path ("dtterm");
662       if (check == NULL)
663         {
664           check = g_strdup ("xterm");
665           g_warning ("couldn't find a terminal, falling back to xterm");
666         }
667       term_argv[0] = check;
668       term_argv[1] = g_strdup ("-e");
669     }
670
671   real_argc = term_argc + *argc;
672   real_argv = g_new (char *, real_argc + 1);
673   
674   for (i = 0; i < term_argc; i++)
675     real_argv[i] = term_argv[i];
676   
677   for (j = 0; j < *argc; j++, i++)
678     real_argv[i] = (char *)the_argv[j];
679   
680   real_argv[i] = NULL;
681   
682   g_free (*argv);
683   *argv = real_argv;
684   *argc = real_argc;
685   
686   /* we use g_free here as we sucked all the inner strings
687    * out from it into real_argv */
688   g_free (term_argv);
689   return TRUE;
690 #else
691   return FALSE;
692 #endif /* G_OS_WIN32 */
693 }
694
695 /* '=' is the new '\0'.
696  * DO NOT CALL unless at least one string ends with '='
697  */
698 static gboolean
699 is_env (const char *a,
700         const char *b)
701 {
702   while (*a == *b)
703   {
704     if (*a == 0 || *b == 0)
705       return FALSE;
706     
707     if (*a == '=')
708       return TRUE;
709
710     a++;
711     b++;
712   }
713
714   return FALSE;
715 }
716
717 /* free with g_strfreev */
718 static char **
719 replace_env_var (char       **old_environ,
720                  const char  *env_var,
721                  const char  *new_value)
722 {
723   int length, new_length;
724   int index, new_index;
725   char **new_environ;
726   int i, new_i;
727
728   /* do two things at once:
729    *  - discover the length of the environment ('length')
730    *  - find the location (if any) of the env var ('index')
731    */
732   index = -1;
733   for (length = 0; old_environ[length]; length++)
734     {
735       /* if we already have it in our environment, replace */
736       if (is_env (old_environ[length], env_var))
737         index = length;
738     }
739
740   
741   /* no current env var, no desired env value.
742    * this is easy :)
743    */
744   if (new_value == NULL && index == -1)
745     return old_environ;
746
747   /* in all cases now, we will be using a modified environment.
748    * determine its length and allocated it.
749    * 
750    * after this block:
751    *   new_index   = location to insert, if any
752    *   new_length  = length of the new array
753    *   new_environ = the pointer array for the new environment
754    */
755   
756   if (new_value == NULL && index >= 0)
757     {
758       /* in this case, we will be removing an entry */
759       new_length = length - 1;
760       new_index = -1;
761     }
762   else if (new_value != NULL && index < 0)
763     {
764       /* in this case, we will be adding an entry to the end */
765       new_length = length + 1;
766       new_index = length;
767     }
768   else
769     /* in this case, we will be replacing the existing entry */
770     {
771       new_length = length;
772       new_index = index;
773     }
774
775   new_environ = g_malloc (sizeof (char *) * (new_length + 1));
776   new_environ[new_length] = NULL;
777
778   /* now we do the copying.
779    * for each entry in the new environment, we decide what to do
780    */
781   
782   i = 0;
783   for (new_i = 0; new_i < new_length; new_i++)
784     {
785       if (new_i == new_index)
786         {
787           /* insert our new item */
788           new_environ[new_i] = g_strconcat (env_var,
789                                             "=",
790                                             new_value,
791                                             NULL);
792           
793           /* if we had an old entry, skip it now */
794           if (index >= 0)
795             i++;
796         }
797       else
798         {
799           /* if this is the old DESKTOP_STARTUP_ID, skip it */
800           if (i == index)
801             i++;
802           
803           /* copy an old item */
804           new_environ[new_i] = g_strdup (old_environ[i]);
805           i++;
806         }
807     }
808
809   g_strfreev (old_environ);
810   
811   return new_environ;
812 }
813
814 static GList *
815 dup_list_segment (GList *start,
816                   GList *end)
817 {
818   GList *res;
819
820   res = NULL;
821   while (start != NULL && start != end)
822     {
823       res = g_list_prepend (res, start->data);
824       start = start->next;
825     }
826
827   return g_list_reverse (res);
828 }
829
830 #ifdef HAVE__NSGETENVIRON
831 #define environ (*_NSGetEnviron())
832 #elif !defined(G_OS_WIN32)
833
834 /* According to the Single Unix Specification, environ is not in 
835  *  * any system header, although unistd.h often declares it.
836  *   */
837 extern char **environ;
838 #endif
839
840 static gboolean
841 g_desktop_app_info_launch (GAppInfo           *appinfo,
842                            GList              *files,
843                            GAppLaunchContext  *launch_context,
844                            GError            **error)
845 {
846   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
847   gboolean completed = FALSE;
848   GList *old_files;
849   GList *launched_files;
850   char **envp;
851   char **argv;
852   int argc;
853   char *display;
854   char *sn_id;
855
856   g_return_val_if_fail (appinfo != NULL, FALSE);
857
858   argv = NULL;
859   envp = NULL;
860       
861   do 
862     {
863       old_files = files;
864       if (!expand_application_parameters (info, &files,
865                                           &argc, &argv, error))
866         goto out;
867       
868       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
869         {
870           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
871                        _("Unable to find terminal required for application"));
872           goto out;
873         }
874
875       sn_id = NULL;
876       if (launch_context)
877         {
878           launched_files = dup_list_segment (old_files, files);
879           
880           display = g_app_launch_context_get_display (launch_context,
881                                                       appinfo,
882                                                       launched_files);
883
884           sn_id = NULL;
885           if (info->startup_notify)
886             sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
887                                                                 appinfo,
888                                                                 launched_files);
889           
890           if (display || sn_id)
891             {
892 #ifdef G_OS_WIN32
893               /* FIXME */
894               envp = g_new0 (char *, 1);
895 #else
896               envp = g_strdupv (environ);
897 #endif
898               
899               if (display)
900                 envp = replace_env_var (envp,
901                                         "DISPLAY",
902                                         display);
903               
904               if (sn_id)
905                 envp = replace_env_var (envp,
906                                         "DESKTOP_STARTUP_ID",
907                                         sn_id);
908             }
909
910           g_free (display);
911           
912           g_list_free (launched_files);
913         }
914       
915       if (!g_spawn_async (info->path,  /* working directory */
916                           argv,
917                           envp,
918                           G_SPAWN_SEARCH_PATH /* flags */,
919                           NULL /* child_setup */,
920                           NULL /* data */,
921                           NULL /* child_pid */,
922                           error))
923         {
924           if (sn_id)
925             {
926               g_app_launch_context_launch_failed (launch_context, sn_id);
927               g_free (sn_id);
928             }
929           goto out;
930         }
931
932       
933       g_free (sn_id);
934       
935       g_strfreev (envp);
936       g_strfreev (argv);
937       envp = NULL;
938       argv = NULL;
939     }
940   while (files != NULL);
941
942   completed = TRUE;
943
944  out:
945   g_strfreev (argv);
946   g_strfreev (envp);
947
948   return completed;
949 }
950
951 static gboolean
952 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
953 {
954   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
955  
956   return info->exec && 
957     ((strstr (info->exec, "%u") != NULL) ||
958      (strstr (info->exec, "%U") != NULL));
959 }
960
961 static gboolean
962 g_desktop_app_info_supports_files (GAppInfo *appinfo)
963 {
964   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
965  
966   return info->exec && 
967     ((strstr (info->exec, "%f") != NULL) ||
968      (strstr (info->exec, "%F") != NULL));
969 }
970
971 static gboolean
972 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
973                                 GList              *uris,
974                                 GAppLaunchContext  *launch_context,
975                                 GError            **error)
976 {
977   GList *files;
978   GFile *file;
979   gboolean res;
980
981   files = NULL;
982   while (uris)
983     {
984       file = g_file_new_for_uri (uris->data);
985       if (file == NULL)
986         g_warning ("Invalid uri passed to g_desktop_app_info_launch_uris");
987       
988       if (file)
989         files = g_list_prepend (files, file);
990     }
991   
992   files = g_list_reverse (files);
993   
994   res = g_desktop_app_info_launch (appinfo, files, launch_context, error);
995   
996   g_list_foreach  (files, (GFunc)g_object_unref, NULL);
997   g_list_free (files);
998   
999   return res;
1000 }
1001
1002 G_LOCK_DEFINE_STATIC (g_desktop_env);
1003 static gchar *g_desktop_env = NULL;
1004
1005 /**
1006  * g_desktop_app_info_set_desktop_env:
1007  * @desktop_env: a string specifying what desktop this is
1008  *
1009  * Sets the name of the desktop that the application is running in.
1010  * This is used by g_app_info_should_show() to evaluate the
1011  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1012  * desktop entry fields.
1013  *
1014  * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop 
1015  * Menu specification</ulink> recognizes the following:
1016  * <simplelist>
1017  *   <member>GNOME</member>
1018  *   <member>KDE</member>
1019  *   <member>ROX</member>
1020  *   <member>XFCE</member>
1021  *   <member>Old</member> 
1022  * </simplelist>
1023  *
1024  * Should be called only once; subsequent calls are ignored.
1025  */
1026 void
1027 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1028 {
1029   G_LOCK (g_desktop_env);
1030   if (!g_desktop_env)
1031     g_desktop_env = g_strdup (desktop_env);
1032   G_UNLOCK (g_desktop_env);
1033 }
1034
1035 static gboolean
1036 g_desktop_app_info_should_show (GAppInfo *appinfo)
1037 {
1038   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1039   gboolean found;
1040   const gchar *desktop_env;
1041   int i;
1042
1043   if (info->nodisplay)
1044     return FALSE;
1045
1046   G_LOCK (g_desktop_env);
1047   desktop_env = g_desktop_env;
1048   G_UNLOCK (g_desktop_env);
1049
1050   if (info->only_show_in)
1051     {
1052       if (desktop_env == NULL)
1053         return FALSE;
1054       
1055       found = FALSE;
1056       for (i = 0; info->only_show_in[i] != NULL; i++)
1057         {
1058           if (strcmp (info->only_show_in[i], desktop_env) == 0)
1059             {
1060               found = TRUE;
1061               break;
1062             }
1063         }
1064       if (!found)
1065         return FALSE;
1066     }
1067
1068   if (info->not_show_in && desktop_env)
1069     {
1070       for (i = 0; info->not_show_in[i] != NULL; i++)
1071         {
1072           if (strcmp (info->not_show_in[i], desktop_env) == 0)
1073             return FALSE;
1074         }
1075     }
1076   
1077   return TRUE;
1078 }
1079
1080 typedef enum {
1081   APP_DIR,
1082   MIMETYPE_DIR
1083 } DirType;
1084
1085 static char *
1086 ensure_dir (DirType   type,
1087             GError  **error)
1088 {
1089   char *path, *display_name;
1090   int err;
1091
1092   if (type == APP_DIR)
1093     path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1094   else
1095     path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1096
1097   errno = 0;
1098   if (g_mkdir_with_parents (path, 0700) == 0)
1099     return path;
1100
1101   err = errno;
1102   display_name = g_filename_display_name (path);
1103   if (type == APP_DIR)
1104     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (err),
1105                  _("Can't create user application configuration folder %s: %s"),
1106                  display_name, g_strerror (err));
1107   else
1108     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (err),
1109                  _("Can't create user MIME configuration folder %s: %s"),
1110                  display_name, g_strerror (err));
1111
1112   g_free (display_name);
1113   g_free (path);
1114
1115   return NULL;
1116 }
1117
1118 static gboolean
1119 update_default_list (const char  *desktop_id, 
1120                      const char  *content_type, 
1121                      gboolean     add, 
1122                      GError     **error)
1123 {
1124   char *dirname, *filename;
1125   GKeyFile *key_file;
1126   gboolean load_succeeded, res;
1127   char **old_list;
1128   char **list;
1129   gsize length, data_size;
1130   char *data;
1131   int i, j;
1132
1133   dirname = ensure_dir (APP_DIR, error);
1134   if (!dirname)
1135     return FALSE;
1136
1137   filename = g_build_filename (dirname, "defaults.list", NULL);
1138   g_free (dirname);
1139
1140   key_file = g_key_file_new ();
1141   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1142   if (!load_succeeded || !g_key_file_has_group (key_file, DEFAULT_APPLICATIONS_GROUP))
1143     {
1144       g_key_file_free (key_file);
1145       key_file = g_key_file_new ();
1146     }
1147
1148   length = 0;
1149   old_list = g_key_file_get_string_list (key_file, DEFAULT_APPLICATIONS_GROUP,
1150                                          content_type, &length, NULL);
1151
1152   list = g_new (char *, 1 + length + 1);
1153
1154   i = 0;
1155   if (add)
1156     list[i++] = g_strdup (desktop_id);
1157   if (old_list)
1158     {
1159       for (j = 0; old_list[j] != NULL; j++)
1160         {
1161           if (strcmp (old_list[j], desktop_id) != 0)
1162             list[i++] = g_strdup (old_list[j]);
1163         }
1164     }
1165   list[i] = NULL;
1166   
1167   g_strfreev (old_list);
1168
1169   g_key_file_set_string_list (key_file,
1170                               DEFAULT_APPLICATIONS_GROUP,
1171                               content_type,
1172                               (const char * const *)list, i);
1173
1174   g_strfreev (list);
1175   
1176   data = g_key_file_to_data (key_file, &data_size, error);
1177   g_key_file_free (key_file);
1178   
1179   res = g_file_set_contents (filename, data, data_size, error);
1180
1181   mime_info_cache_reload (NULL);
1182                           
1183   g_free (filename);
1184   g_free (data);
1185   
1186   return res;
1187 }
1188
1189 static gboolean
1190 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
1191                                             const char  *content_type,
1192                                             GError     **error)
1193 {
1194   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1195
1196   if (!g_app_info_add_supports_type (appinfo, content_type, error))
1197     return FALSE;
1198   
1199   return update_default_list (info->desktop_id, content_type, TRUE, error);
1200 }
1201
1202 static void
1203 update_program_done (GPid     pid,
1204                      gint     status,
1205                      gpointer data)
1206 {
1207   /* Did the application exit correctly */
1208   if (WIFEXITED (status) &&
1209       WEXITSTATUS (status) == 0)
1210     {
1211       /* Here we could clean out any caches in use */
1212     }
1213 }
1214
1215 static void
1216 run_update_command (char *command,
1217                     char *subdir)
1218 {
1219         char *argv[3] = {
1220                 NULL,
1221                 NULL,
1222                 NULL,
1223         };
1224         GPid pid = 0;
1225         GError *error = NULL;
1226
1227         argv[0] = command;
1228         argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1229
1230         if (g_spawn_async ("/", argv,
1231                            NULL,       /* envp */
1232                            G_SPAWN_SEARCH_PATH |
1233                            G_SPAWN_STDOUT_TO_DEV_NULL |
1234                            G_SPAWN_STDERR_TO_DEV_NULL |
1235                            G_SPAWN_DO_NOT_REAP_CHILD,
1236                            NULL, NULL, /* No setup function */
1237                            &pid,
1238                            NULL)) 
1239           g_child_watch_add (pid, update_program_done, NULL);
1240         else
1241           {
1242             /* If we get an error at this point, it's quite likely the user doesn't
1243              * have an installed copy of either 'update-mime-database' or
1244              * 'update-desktop-database'.  I don't think we want to popup an error
1245              * dialog at this point, so we just do a g_warning to give the user a
1246              * chance of debugging it.
1247              */
1248             g_warning ("%s", error->message);
1249           }
1250         
1251         g_free (argv[1]);
1252 }
1253
1254 static gboolean
1255 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
1256                                                  const char  *extension,
1257                                                  GError     **error)
1258 {
1259   char *filename, *basename, *mimetype;
1260   char *dirname;
1261   gboolean res;
1262
1263   dirname = ensure_dir (MIMETYPE_DIR, error);
1264   if (!dirname)
1265     return FALSE;
1266   
1267   basename = g_strdup_printf ("user-extension-%s.xml", extension);
1268   filename = g_build_filename (dirname, basename, NULL);
1269   g_free (basename);
1270   g_free (dirname);
1271
1272   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1273   
1274   if (!g_file_test (filename, G_FILE_TEST_EXISTS)) 
1275     {
1276       char *contents;
1277
1278       contents =
1279         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1280                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1281                          " <mime-type type=\"%s\">\n"
1282                          "  <comment>%s document</comment>\n"
1283                          "  <glob pattern=\"*.%s\"/>\n"
1284                          " </mime-type>\n"
1285                          "</mime-info>\n", mimetype, extension, extension);
1286
1287       g_file_set_contents (filename, contents, -1, NULL);
1288       g_free (contents);
1289
1290       run_update_command ("update-mime-database", "mime");
1291     }
1292   g_free (filename);
1293   
1294   res = g_desktop_app_info_set_as_default_for_type (appinfo,
1295                                                     mimetype,
1296                                                     error);
1297
1298   g_free (mimetype);
1299   
1300   return res;
1301 }
1302
1303 static gboolean
1304 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
1305                                       const char  *content_type,
1306                                       GError     **error)
1307 {
1308   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1309   GKeyFile *keyfile;
1310   char *new_mimetypes, *old_mimetypes, *content;
1311   char *dirname;
1312   char *filename;
1313
1314   keyfile = g_key_file_new ();
1315   if (!g_key_file_load_from_file (keyfile, info->filename,
1316                                   G_KEY_FILE_KEEP_COMMENTS |
1317                                   G_KEY_FILE_KEEP_TRANSLATIONS, error))
1318     {
1319       g_key_file_free (keyfile);
1320       return FALSE;
1321     }
1322
1323   old_mimetypes = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL);
1324   new_mimetypes = g_strconcat (content_type, ";", old_mimetypes, NULL);
1325   g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, new_mimetypes);
1326   g_free (old_mimetypes);
1327   g_free (new_mimetypes);
1328
1329   content = g_key_file_to_data (keyfile, NULL, NULL);
1330   g_key_file_free (keyfile);
1331
1332   dirname = ensure_dir (APP_DIR, error);
1333   if (!dirname)
1334     {
1335       g_free (content);
1336       return FALSE;
1337     }
1338   
1339   filename = g_build_filename (dirname, info->desktop_id, NULL);
1340   g_free (dirname);
1341   
1342   if (!g_file_set_contents (filename, content, -1, error))
1343     {
1344       g_free (filename);
1345       g_free (content);
1346       return FALSE;
1347     }
1348   g_free (filename);
1349   g_free (content);
1350
1351   run_update_command ("update-desktop-database", "applications");
1352   return TRUE;
1353 }
1354
1355 static gboolean
1356 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1357 {
1358   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1359   char *user_dirname;
1360   
1361   user_dirname = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1362   return g_str_has_prefix (info->filename, user_dirname);
1363 }
1364
1365 static gboolean
1366 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
1367                                          const char  *content_type,
1368                                          GError     **error)
1369 {
1370   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1371   GKeyFile *keyfile;
1372   char *new_mimetypes, *old_mimetypes, *content;
1373   char *found;
1374   char *filename;
1375   char *dirname;
1376
1377   keyfile = g_key_file_new ();
1378   if (!g_key_file_load_from_file (keyfile, info->filename,
1379                                   G_KEY_FILE_KEEP_COMMENTS |
1380                                   G_KEY_FILE_KEEP_TRANSLATIONS, error))
1381     {
1382       g_key_file_free (keyfile);
1383       return FALSE;
1384     }
1385
1386   old_mimetypes = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL);
1387   new_mimetypes = g_strdup (old_mimetypes);
1388   found = NULL;
1389   if (new_mimetypes)
1390     found = strstr (new_mimetypes, content_type);
1391   if (found && *(found + strlen (content_type)) == ';')
1392     {
1393       char *rest = found + strlen (content_type) + 1;
1394       memmove (found, rest, strlen (rest) + 1);
1395     }
1396   g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, new_mimetypes);
1397   g_free (old_mimetypes);
1398   g_free (new_mimetypes);
1399
1400   content = g_key_file_to_data (keyfile, NULL, NULL);
1401   g_key_file_free (keyfile);
1402
1403   dirname = ensure_dir (APP_DIR, error);
1404   if (!dirname)
1405     {
1406       g_free (content);
1407       return FALSE;
1408     }
1409   
1410   filename = g_build_filename (dirname, info->desktop_id, NULL);
1411   g_free (dirname);
1412   if (!g_file_set_contents (filename, content, -1, error))
1413     {
1414       g_free (filename);
1415       g_free (content);
1416       return FALSE;
1417     }
1418   g_free (filename);
1419   g_free (content);
1420
1421   run_update_command ("update-desktop-database", "applications");
1422
1423   return update_default_list (info->desktop_id, content_type, FALSE, error);
1424 }
1425
1426 /**
1427  * g_app_info_create_from_commandline:
1428  * @commandline: the commandline to use
1429  * @application_name: the application name, or %NULL to use @commandline
1430  * @flags: flags that can specify details of the created #GAppInfo
1431  * @error: a #GError location to store the error occuring, %NULL to ignore.
1432  *
1433  * Creates a new #GAppInfo from the given information.
1434  *
1435  * Returns: new #GAppInfo for given command.
1436  **/
1437 GAppInfo *
1438 g_app_info_create_from_commandline (const char           *commandline,
1439                                     const char           *application_name,
1440                                     GAppInfoCreateFlags   flags,
1441                                     GError              **error)
1442 {
1443   GKeyFile *key_file;
1444   char *dirname;
1445   char **split;
1446   char *basename, *exec, *filename, *comment;
1447   char *data, *desktop_id;
1448   gsize data_size;
1449   int fd;
1450   GDesktopAppInfo *info;
1451   gboolean res;
1452
1453   dirname = ensure_dir (APP_DIR, error);
1454   if (!dirname)
1455     return NULL;
1456   
1457   key_file = g_key_file_new ();
1458
1459   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1460                          "Encoding", "UTF-8");
1461   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1462                          G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1463   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1464                          G_KEY_FILE_DESKTOP_KEY_TYPE,
1465                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1466   if (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) 
1467     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1468                             G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1469
1470   exec = g_strconcat (commandline, " %f", NULL);
1471   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1472                          G_KEY_FILE_DESKTOP_KEY_EXEC, exec);
1473   g_free (exec);
1474
1475   /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1476   split = g_strsplit (commandline, " ", 2);
1477   basename = g_path_get_basename (split[0]);
1478   g_strfreev (split);
1479   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1480                          G_KEY_FILE_DESKTOP_KEY_NAME, application_name?application_name:basename);
1481
1482   comment = g_strdup_printf (_("Custom definition for %s"), basename);
1483   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1484                          G_KEY_FILE_DESKTOP_KEY_COMMENT, comment);
1485   g_free (comment);
1486   
1487   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1488                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1489
1490   data = g_key_file_to_data (key_file, &data_size, NULL);
1491   g_key_file_free (key_file);
1492
1493   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", basename);
1494   g_free (basename);
1495   filename = g_build_filename (dirname, desktop_id, NULL);
1496   g_free (desktop_id);
1497   g_free (dirname);
1498   
1499   fd = g_mkstemp (filename);
1500   if (fd == -1)
1501     {
1502       char *display_name;
1503
1504       display_name = g_filename_display_name (filename);
1505       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1506                    _("Can't create user desktop file %s"), display_name);
1507       g_free (display_name);
1508       g_free (filename);
1509       g_free (data);
1510       return NULL;
1511     }
1512
1513   desktop_id = g_path_get_basename (filename);
1514
1515   close (fd);
1516   
1517   res = g_file_set_contents (filename, data, data_size, error);
1518   if (!res)
1519     {
1520       g_free (desktop_id);
1521       g_free (filename);
1522       return NULL;
1523     }
1524
1525   run_update_command ("update-desktop-database", "applications");
1526   
1527   info = g_desktop_app_info_new_from_filename (filename);
1528   g_free (filename);
1529   if (info == NULL) 
1530     g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1531                  _("Can't load just created desktop file"));
1532   else
1533     info->desktop_id = g_strdup (desktop_id);
1534     
1535   g_free (desktop_id);
1536   
1537   return G_APP_INFO (info);
1538 }
1539
1540
1541 static void
1542 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1543 {
1544   iface->dup = g_desktop_app_info_dup;
1545   iface->equal = g_desktop_app_info_equal;
1546   iface->get_id = g_desktop_app_info_get_id;
1547   iface->get_name = g_desktop_app_info_get_name;
1548   iface->get_description = g_desktop_app_info_get_description;
1549   iface->get_executable = g_desktop_app_info_get_executable;
1550   iface->get_icon = g_desktop_app_info_get_icon;
1551   iface->launch = g_desktop_app_info_launch;
1552   iface->supports_uris = g_desktop_app_info_supports_uris;
1553   iface->supports_files = g_desktop_app_info_supports_files;
1554   iface->launch_uris = g_desktop_app_info_launch_uris;
1555   iface->should_show = g_desktop_app_info_should_show;
1556   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1557   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1558   iface->add_supports_type = g_desktop_app_info_add_supports_type;
1559   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1560   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1561 }
1562
1563 static gboolean
1564 app_info_in_list (GAppInfo *info, 
1565                   GList    *list)
1566 {
1567   while (list != NULL)
1568     {
1569       if (g_app_info_equal (info, list->data))
1570         return TRUE;
1571       list = list->next;
1572     }
1573   return FALSE;
1574 }
1575
1576
1577 /**
1578  * g_app_info_get_all_for_type:
1579  * @content_type: the content type to find a #GAppInfo for
1580  * 
1581  * Gets a list of all #GAppInfo s for a given content type.
1582  *
1583  * Returns: #GList of #GAppInfo s for given @content_type
1584  *    or %NULL on error.
1585  **/
1586 GList *
1587 g_app_info_get_all_for_type (const char *content_type)
1588 {
1589   GList *desktop_entries, *l;
1590   GList *infos;
1591   GDesktopAppInfo *info;
1592
1593   g_return_val_if_fail (content_type != NULL, NULL);
1594   
1595   desktop_entries = get_all_desktop_entries_for_mime_type (content_type);
1596
1597   infos = NULL;
1598   for (l = desktop_entries; l != NULL; l = l->next)
1599     {
1600       char *desktop_entry = l->data;
1601
1602       info = g_desktop_app_info_new (desktop_entry);
1603       if (info)
1604         {
1605           if (app_info_in_list (G_APP_INFO (info), infos))
1606             g_object_unref (info);
1607           else
1608             infos = g_list_prepend (infos, info);
1609         }
1610       g_free (desktop_entry);
1611     }
1612
1613   g_list_free (desktop_entries);
1614   
1615   return g_list_reverse (infos);
1616 }
1617
1618
1619 /**
1620  * g_app_info_get_default_for_type:
1621  * @content_type: the content type to find a #GAppInfo for
1622  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1623  *     support URIs
1624  * 
1625  * Gets the #GAppInfo that correspond to a given content type.
1626  *
1627  * Returns: #GAppInfo for given @content_type or %NULL on error.
1628  **/
1629 GAppInfo *
1630 g_app_info_get_default_for_type (const char *content_type,
1631                                  gboolean    must_support_uris)
1632 {
1633   GList *desktop_entries, *l;
1634   GAppInfo *info;
1635
1636   g_return_val_if_fail (content_type != NULL, NULL);
1637   
1638   desktop_entries = get_all_desktop_entries_for_mime_type (content_type);
1639
1640   info = NULL;
1641   for (l = desktop_entries; l != NULL; l = l->next)
1642     {
1643       char *desktop_entry = l->data;
1644
1645       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1646       if (info)
1647         {
1648           if (must_support_uris && !g_app_info_supports_uris (info))
1649             {
1650               g_object_unref (info);
1651               info = NULL;
1652             }
1653           else
1654             break;
1655         }
1656     }
1657   
1658   g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
1659   g_list_free (desktop_entries);
1660   
1661   return info;
1662 }
1663
1664
1665 /**
1666  * g_app_info_get_default_for_uri_scheme:
1667  * @uri_scheme: a string containing a URI scheme.
1668  *
1669  * Gets the default application for launching applications 
1670  * using this URI scheme.
1671  *
1672  * TODO: This is currently unimplemented.
1673  * 
1674  * Returns: %NULL.
1675  **/
1676 GAppInfo *
1677 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1678 {
1679   /* TODO: Implement this using giomodules, reading the gconf settings
1680    * in /desktop/gnome/url-handlers
1681    */
1682   return NULL;
1683 }
1684
1685
1686 static void
1687 get_apps_from_dir (GHashTable *apps, 
1688                    const char *dirname, 
1689                    const char *prefix)
1690 {
1691   GDir *dir;
1692   const char *basename;
1693   char *filename, *subprefix, *desktop_id;
1694   gboolean hidden;
1695   GDesktopAppInfo *appinfo;
1696   
1697   dir = g_dir_open (dirname, 0, NULL);
1698   if (dir)
1699     {
1700       while ((basename = g_dir_read_name (dir)) != NULL)
1701         {
1702           filename = g_build_filename (dirname, basename, NULL);
1703           if (g_str_has_suffix (basename, ".desktop"))
1704             {
1705               desktop_id = g_strconcat (prefix, basename, NULL);
1706
1707               /* Use _extended so we catch NULLs too (hidden) */
1708               if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1709                 {
1710                   appinfo = g_desktop_app_info_new_from_filename (filename);
1711
1712                   if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1713                     {
1714                       g_object_unref (appinfo);
1715                       appinfo = NULL;
1716                       hidden = TRUE;
1717                     }
1718                                       
1719                   if (appinfo || hidden)
1720                     {
1721                       g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1722
1723                       if (appinfo)
1724                         {
1725                           /* Reuse instead of strdup here */
1726                           appinfo->desktop_id = desktop_id;
1727                           desktop_id = NULL;
1728                         }
1729                     }
1730                 }
1731               g_free (desktop_id);
1732             }
1733           else
1734             {
1735               if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1736                 {
1737                   subprefix = g_strconcat (prefix, basename, "-", NULL);
1738                   get_apps_from_dir (apps, filename, subprefix);
1739                   g_free (subprefix);
1740                 }
1741             }
1742           g_free (filename);
1743         }
1744       g_dir_close (dir);
1745     }
1746 }
1747
1748
1749 /**
1750  * g_app_info_get_all:
1751  *
1752  * Gets a list of all of the applications currently registered 
1753  * on this system.
1754  * 
1755  * For desktop files, this includes applications that have 
1756  * <literal>NoDisplay=true</literal> set or are excluded from 
1757  * display by means of <literal>OnlyShowIn</literal> or
1758  * <literal>NotShowIn</literal>. See g_app_info_should_show().
1759  * The returned list does not include applications which have
1760  * the <literal>Hidden</literal> key set. 
1761  * 
1762  * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
1763  **/
1764 GList *
1765 g_app_info_get_all (void)
1766 {
1767   const char * const *dirs;
1768   GHashTable *apps;
1769   GHashTableIter iter;
1770   gpointer value;
1771   int i;
1772   GList *infos;
1773
1774   dirs = get_applications_search_path ();
1775
1776   apps = g_hash_table_new_full (g_str_hash, g_str_equal,
1777                                 g_free, NULL);
1778
1779   
1780   for (i = 0; dirs[i] != NULL; i++)
1781     get_apps_from_dir (apps, dirs[i], "");
1782
1783
1784   infos = NULL;
1785   g_hash_table_iter_init (&iter, apps);
1786   while (g_hash_table_iter_next (&iter, NULL, &value))
1787     {
1788       if (value)
1789         infos = g_list_prepend (infos, value);
1790     }
1791
1792   g_hash_table_destroy (apps);
1793
1794   return g_list_reverse (infos);
1795 }
1796
1797 /* Cacheing of mimeinfo.cache and defaults.list files */
1798
1799 typedef struct {
1800   char *path;
1801   GHashTable *mime_info_cache_map;
1802   GHashTable *defaults_list_map;
1803   time_t mime_info_cache_timestamp;
1804   time_t defaults_list_timestamp;
1805 } MimeInfoCacheDir;
1806
1807 typedef struct {
1808   GList *dirs;                       /* mimeinfo.cache and defaults.list */
1809   GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
1810   time_t last_stat_time;
1811   guint should_ping_mime_monitor : 1;
1812 } MimeInfoCache;
1813
1814 static MimeInfoCache *mime_info_cache = NULL;
1815 G_LOCK_DEFINE_STATIC (mime_info_cache);
1816
1817 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
1818                                                      const char        *mime_type,
1819                                                      char             **new_desktop_file_ids);
1820
1821 static MimeInfoCache * mime_info_cache_new (void);
1822
1823 static void
1824 destroy_info_cache_value (gpointer  key, 
1825                           GList    *value, 
1826                           gpointer  data)
1827 {
1828   g_list_foreach (value, (GFunc)g_free, NULL);
1829   g_list_free (value);
1830 }
1831
1832 static void
1833 destroy_info_cache_map (GHashTable *info_cache_map)
1834 {
1835   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
1836   g_hash_table_destroy (info_cache_map);
1837 }
1838
1839 static gboolean
1840 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
1841                                  const char       *cache_file,
1842                                  time_t           *timestamp)
1843 {
1844   struct stat buf;
1845   char *filename;
1846   
1847   filename = g_build_filename (dir->path, cache_file, NULL);
1848   
1849   if (g_stat (filename, &buf) < 0)
1850     {
1851       g_free (filename);
1852       return TRUE;
1853     }
1854   g_free (filename);
1855
1856   if (buf.st_mtime != *timestamp) 
1857     return TRUE;
1858   
1859   return FALSE;
1860 }
1861
1862 /* Call with lock held */
1863 static gboolean
1864 remove_all (gpointer  key,
1865             gpointer  value,
1866             gpointer  user_data)
1867 {
1868   return TRUE;
1869 }
1870
1871
1872 static void
1873 mime_info_cache_blow_global_cache (void)
1874 {
1875   g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
1876                                remove_all, NULL);
1877 }
1878
1879 static void
1880 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
1881 {
1882   GError *load_error;
1883   GKeyFile *key_file;
1884   gchar *filename, **mime_types;
1885   int i;
1886   struct stat buf;
1887   
1888   load_error = NULL;
1889   mime_types = NULL;
1890   
1891   if (dir->mime_info_cache_map != NULL &&
1892       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
1893                                         &dir->mime_info_cache_timestamp))
1894     return;
1895   
1896   if (dir->mime_info_cache_map != NULL)
1897     destroy_info_cache_map (dir->mime_info_cache_map);
1898   
1899   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
1900                                                     (GDestroyNotify) g_free,
1901                                                     NULL);
1902   
1903   key_file = g_key_file_new ();
1904   
1905   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
1906   
1907   if (g_stat (filename, &buf) < 0)
1908     goto error;
1909   
1910   if (dir->mime_info_cache_timestamp > 0) 
1911     mime_info_cache->should_ping_mime_monitor = TRUE;
1912   
1913   dir->mime_info_cache_timestamp = buf.st_mtime;
1914   
1915   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
1916   
1917   g_free (filename);
1918   filename = NULL;
1919   
1920   if (load_error != NULL)
1921     goto error;
1922   
1923   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
1924                                     NULL, &load_error);
1925   
1926   if (load_error != NULL)
1927     goto error;
1928   
1929   for (i = 0; mime_types[i] != NULL; i++)
1930     {
1931       gchar **desktop_file_ids;
1932       char *unaliased_type;
1933       desktop_file_ids = g_key_file_get_string_list (key_file,
1934                                                      MIME_CACHE_GROUP,
1935                                                      mime_types[i],
1936                                                      NULL,
1937                                                      NULL);
1938       
1939       if (desktop_file_ids == NULL)
1940         continue;
1941
1942       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
1943       mime_info_cache_dir_add_desktop_entries (dir,
1944                                                unaliased_type,
1945                                                desktop_file_ids);
1946       g_free (unaliased_type);
1947     
1948       g_strfreev (desktop_file_ids);
1949     }
1950   
1951   g_strfreev (mime_types);
1952   g_key_file_free (key_file);
1953   
1954   return;
1955  error:
1956   g_free (filename);
1957   g_key_file_free (key_file);
1958   
1959   if (mime_types != NULL)
1960     g_strfreev (mime_types);
1961   
1962   if (load_error)
1963     g_error_free (load_error);
1964 }
1965
1966 static void
1967 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
1968 {
1969   GKeyFile *key_file;
1970   GError *load_error;
1971   gchar *filename, **mime_types;
1972   char *unaliased_type;
1973   char **desktop_file_ids;
1974   int i;
1975   struct stat buf;
1976
1977   load_error = NULL;
1978   mime_types = NULL;
1979
1980   if (dir->defaults_list_map != NULL &&
1981       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
1982                                         &dir->defaults_list_timestamp))
1983     return;
1984   
1985   if (dir->defaults_list_map != NULL)
1986     g_hash_table_destroy (dir->defaults_list_map);
1987
1988   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
1989                                                   g_free, (GDestroyNotify)g_strfreev);
1990
1991   key_file = g_key_file_new ();
1992   
1993   filename = g_build_filename (dir->path, "defaults.list", NULL);
1994   if (g_stat (filename, &buf) < 0)
1995     goto error;
1996
1997   if (dir->defaults_list_timestamp > 0) 
1998     mime_info_cache->should_ping_mime_monitor = TRUE;
1999
2000   dir->defaults_list_timestamp = buf.st_mtime;
2001
2002   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2003   g_free (filename);
2004   filename = NULL;
2005
2006   if (load_error != NULL)
2007     goto error;
2008
2009   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2010                                     NULL, &load_error);
2011
2012   if (load_error != NULL)
2013     goto error;
2014
2015   for (i = 0; mime_types[i] != NULL; i++)
2016     {
2017       desktop_file_ids = g_key_file_get_string_list (key_file,
2018                                                      DEFAULT_APPLICATIONS_GROUP,
2019                                                      mime_types[i],
2020                                                      NULL,
2021                                                      NULL);
2022       if (desktop_file_ids == NULL)
2023         continue;
2024
2025       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2026       g_hash_table_replace (dir->defaults_list_map,
2027                             unaliased_type,
2028                             desktop_file_ids);
2029     }
2030
2031   g_strfreev (mime_types);
2032   g_key_file_free (key_file);
2033   
2034   return;
2035  error:
2036   g_free (filename);
2037   g_key_file_free (key_file);
2038   
2039   if (mime_types != NULL)
2040     g_strfreev (mime_types);
2041   
2042   if (load_error)
2043     g_error_free (load_error);
2044 }
2045
2046 static MimeInfoCacheDir *
2047 mime_info_cache_dir_new (const char *path)
2048 {
2049   MimeInfoCacheDir *dir;
2050
2051   dir = g_new0 (MimeInfoCacheDir, 1);
2052   dir->path = g_strdup (path);
2053   
2054   return dir;
2055 }
2056
2057 static void
2058 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2059 {
2060   if (dir == NULL)
2061     return;
2062   
2063   if (dir->mime_info_cache_map != NULL)
2064     {
2065       destroy_info_cache_map (dir->mime_info_cache_map);
2066       dir->mime_info_cache_map = NULL;
2067       
2068   }
2069   
2070   if (dir->defaults_list_map != NULL)
2071     {
2072       g_hash_table_destroy (dir->defaults_list_map);
2073       dir->defaults_list_map = NULL;
2074     }
2075   
2076   g_free (dir);
2077 }
2078
2079 static void
2080 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
2081                                          const char        *mime_type,
2082                                          char             **new_desktop_file_ids)
2083 {
2084   GList *desktop_file_ids;
2085   int i;
2086   
2087   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2088                                           mime_type);
2089   
2090   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2091     {
2092       if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2093         desktop_file_ids = g_list_append (desktop_file_ids,
2094                                           g_strdup (new_desktop_file_ids[i]));
2095     }
2096   
2097   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2098 }
2099
2100 static void
2101 mime_info_cache_init_dir_lists (void)
2102 {
2103   const char * const *dirs;
2104   int i;
2105   
2106   mime_info_cache = mime_info_cache_new ();
2107   
2108   dirs = get_applications_search_path ();
2109   
2110   for (i = 0; dirs[i] != NULL; i++)
2111     {
2112       MimeInfoCacheDir *dir;
2113       
2114       dir = mime_info_cache_dir_new (dirs[i]);
2115       
2116       if (dir != NULL)
2117         {
2118           mime_info_cache_dir_init (dir);
2119           mime_info_cache_dir_init_defaults_list (dir);
2120           
2121           mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2122         }
2123     }
2124 }
2125
2126 static void
2127 mime_info_cache_update_dir_lists (void)
2128 {
2129   GList *tmp;
2130   
2131   tmp = mime_info_cache->dirs;
2132   
2133   while (tmp != NULL)
2134     {
2135       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2136
2137       /* No need to do this if we had file monitors... */
2138       mime_info_cache_blow_global_cache ();
2139       mime_info_cache_dir_init (dir);
2140       mime_info_cache_dir_init_defaults_list (dir);
2141       
2142       tmp = tmp->next;
2143     }
2144 }
2145
2146 static void
2147 mime_info_cache_init (void)
2148 {
2149         G_LOCK (mime_info_cache);
2150         if (mime_info_cache == NULL)
2151           mime_info_cache_init_dir_lists ();
2152         else
2153           {
2154             time_t now;
2155             
2156             time (&now);
2157             if (now >= mime_info_cache->last_stat_time + 10)
2158               {
2159                 mime_info_cache_update_dir_lists ();
2160                 mime_info_cache->last_stat_time = now;
2161               }
2162           }
2163
2164         if (mime_info_cache->should_ping_mime_monitor)
2165           {
2166             /* g_idle_add (emit_mime_changed, NULL); */
2167             mime_info_cache->should_ping_mime_monitor = FALSE;
2168           }
2169         
2170         G_UNLOCK (mime_info_cache);
2171 }
2172
2173 static MimeInfoCache *
2174 mime_info_cache_new (void)
2175 {
2176   MimeInfoCache *cache;
2177   
2178   cache = g_new0 (MimeInfoCache, 1);
2179   
2180   cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2181                                                         (GDestroyNotify) g_free,
2182                                                         (GDestroyNotify) g_free);
2183   return cache;
2184 }
2185
2186 static void
2187 mime_info_cache_free (MimeInfoCache *cache)
2188 {
2189   if (cache == NULL)
2190     return;
2191   
2192   g_list_foreach (cache->dirs,
2193                   (GFunc) mime_info_cache_dir_free,
2194                   NULL);
2195   g_list_free (cache->dirs);
2196   g_hash_table_destroy (cache->global_defaults_cache);
2197   g_free (cache);
2198 }
2199
2200 /**
2201  * mime_info_cache_reload:
2202  * @dir: directory path which needs reloading.
2203  * 
2204  * Reload the mime information for the @dir.
2205  */
2206 static void
2207 mime_info_cache_reload (const char *dir)
2208 {
2209   /* FIXME: just reload the dir that needs reloading,
2210    * don't blow the whole cache
2211    */
2212   if (mime_info_cache != NULL)
2213     {
2214       G_LOCK (mime_info_cache);
2215       mime_info_cache_free (mime_info_cache);
2216       mime_info_cache = NULL;
2217       G_UNLOCK (mime_info_cache);
2218     }
2219 }
2220
2221 static GList *
2222 append_desktop_entry (GList      *list, 
2223                       const char *desktop_entry)
2224 {
2225   /* Add if not already in list, and valid */
2226   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp))
2227     list = g_list_prepend (list, g_strdup (desktop_entry));
2228   
2229   return list;
2230 }
2231
2232 /**
2233  * get_all_desktop_entries_for_mime_type:
2234  * @mime_type: a mime type.
2235  *
2236  * Returns all the desktop ids for @mime_type. The desktop files
2237  * are listed in an order so that default applications are listed before
2238  * non-default ones, and handlers for inherited mimetypes are listed
2239  * after the base ones.
2240  *
2241  * Return value: a #GList containing the desktop ids which claim
2242  *    to handle @mime_type.
2243  */
2244 static GList *
2245 get_all_desktop_entries_for_mime_type (const char *base_mime_type)
2246 {
2247   GList *desktop_entries, *list, *dir_list, *tmp;
2248   MimeInfoCacheDir *dir;
2249   char *mime_type;
2250   char **mime_types;
2251   char **default_entries;
2252   int i,j;
2253   
2254   mime_info_cache_init ();
2255
2256   mime_types = _g_unix_content_type_get_parents (base_mime_type);
2257   G_LOCK (mime_info_cache);
2258   
2259   desktop_entries = NULL;
2260   for (i = 0; mime_types[i] != NULL; i++)
2261     {
2262       mime_type = mime_types[i];
2263
2264       /* Go through all apps listed as defaults */
2265       for (dir_list = mime_info_cache->dirs;
2266            dir_list != NULL;
2267            dir_list = dir_list->next)
2268         {
2269           dir = dir_list->data;
2270           default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2271           for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2272             desktop_entries = append_desktop_entry (desktop_entries, default_entries[j]);
2273         }
2274
2275       /* Go through all entries that support the mimetype */
2276       for (dir_list = mime_info_cache->dirs;
2277            dir_list != NULL;
2278            dir_list = dir_list->next) 
2279         {
2280           dir = dir_list->data;
2281         
2282           list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2283           for (tmp = list; tmp != NULL; tmp = tmp->next)
2284             desktop_entries = append_desktop_entry (desktop_entries, tmp->data);
2285         }
2286     }
2287   
2288   G_UNLOCK (mime_info_cache);
2289
2290   g_strfreev (mime_types);
2291   
2292   desktop_entries = g_list_reverse (desktop_entries);
2293   
2294   return desktop_entries;
2295 }
2296
2297 #define __G_DESKTOP_APP_INFO_C__
2298 #include "gioaliasdef.c"