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