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><gio/gdesktopappinfo.h></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