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