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