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