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