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