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