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